FreeRDP
client/location_main.c
1 
21 #include <freerdp/config.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <float.h>
26 #include <math.h>
27 
28 #include <winpr/crt.h>
29 #include <winpr/assert.h>
30 #include <winpr/stream.h>
31 
32 #include <freerdp/client/channels.h>
33 #include <freerdp/channels/log.h>
34 #include <freerdp/channels/location.h>
36 #include <freerdp/utils/encoded_types.h>
37 
38 #define TAG CHANNELS_TAG("location.client")
39 
40 /* implement [MS-RDPEL]
41  * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpel/4397a0af-c821-4b75-9068-476fb579c327
42  */
43 typedef struct
44 {
45  GENERIC_DYNVC_PLUGIN baseDynPlugin;
46  LocationClientContext context;
47 } LOCATION_PLUGIN;
48 
49 typedef struct
50 {
52  UINT32 serverVersion;
53  UINT32 clientVersion;
54  UINT32 serverFlags;
55  UINT32 clientFlags;
56 } LOCATION_CALLBACK;
57 
58 static BOOL location_read_header(wLog* log, wStream* s, UINT16* ppduType, UINT32* ppduLength)
59 {
60  WINPR_ASSERT(log);
61  WINPR_ASSERT(s);
62  WINPR_ASSERT(ppduType);
63  WINPR_ASSERT(ppduLength);
64 
65  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 6))
66  return FALSE;
67  Stream_Read_UINT16(s, *ppduType);
68  Stream_Read_UINT32(s, *ppduLength);
69  if (*ppduLength < 6)
70  {
71  WLog_Print(log, WLOG_ERROR,
72  "RDPLOCATION_HEADER::pduLengh=%" PRIu16 " < sizeof(RDPLOCATION_HEADER)[6]",
73  *ppduLength);
74  return FALSE;
75  }
76  return Stream_CheckAndLogRequiredLengthWLog(log, s, *ppduLength - 6ull);
77 }
78 
79 static BOOL location_write_header(wStream* s, UINT16 pduType, UINT32 pduLength)
80 {
81  if (!Stream_EnsureRemainingCapacity(s, 6))
82  return FALSE;
83  Stream_Write_UINT16(s, pduType);
84  Stream_Write_UINT32(s, pduLength + 6);
85  return Stream_EnsureRemainingCapacity(s, pduLength);
86 }
87 
88 static BOOL location_read_server_ready_pdu(LOCATION_CALLBACK* callback, wStream* s, UINT16 pduSize)
89 {
90  if (pduSize < 6 + 4)
91  return FALSE; // Short message
92 
93  Stream_Read_UINT32(s, callback->serverVersion);
94  if (pduSize >= 6 + 4 + 4)
95  Stream_Read_UINT32(s, callback->serverFlags);
96  return TRUE;
97 }
98 
99 static UINT location_channel_send(IWTSVirtualChannel* channel, wStream* s)
100 {
101  const size_t len = Stream_GetPosition(s);
102  if (len > UINT32_MAX)
103  return ERROR_INTERNAL_ERROR;
104 
105  Stream_SetPosition(s, 2);
106  Stream_Write_UINT32(s, (UINT32)len);
107 
108  WINPR_ASSERT(channel);
109  WINPR_ASSERT(channel->Write);
110  return channel->Write(channel, (UINT32)len, Stream_Buffer(s), NULL);
111 }
112 
113 static UINT location_send_client_ready_pdu(const LOCATION_CALLBACK* callback)
114 {
115  wStream sbuffer = { 0 };
116  BYTE buffer[32] = { 0 };
117  wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
118  WINPR_ASSERT(s);
119 
120  if (!location_write_header(s, PDUTYPE_CLIENT_READY, 8))
121  return ERROR_OUTOFMEMORY;
122 
123  Stream_Write_UINT32(s, callback->clientVersion);
124  Stream_Write_UINT32(s, callback->clientFlags);
125  return location_channel_send(callback->baseCb.channel, s);
126 }
127 
128 static const char* location_version_str(UINT32 version, char* buffer, size_t size)
129 {
130  const char* str = NULL;
131  switch (version)
132  {
133  case RDPLOCATION_PROTOCOL_VERSION_100:
134  str = "RDPLOCATION_PROTOCOL_VERSION_100";
135  break;
136  case RDPLOCATION_PROTOCOL_VERSION_200:
137  str = "RDPLOCATION_PROTOCOL_VERSION_200";
138  break;
139  default:
140  str = "RDPLOCATION_PROTOCOL_VERSION_UNKNOWN";
141  break;
142  }
143 
144  (void)_snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, version);
145  return buffer;
146 }
147 
153 static UINT location_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
154 {
155  LOCATION_CALLBACK* callback = (LOCATION_CALLBACK*)pChannelCallback;
156 
157  WINPR_ASSERT(callback);
158 
159  LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->baseCb.plugin;
160  WINPR_ASSERT(plugin);
161 
162  UINT16 pduType = 0;
163  UINT32 pduLength = 0;
164  if (!location_read_header(plugin->baseDynPlugin.log, data, &pduType, &pduLength))
165  return ERROR_INVALID_DATA;
166 
167  switch (pduType)
168  {
169  case PDUTYPE_SERVER_READY:
170  if (!location_read_server_ready_pdu(callback, data, pduLength))
171  return ERROR_INVALID_DATA;
172 
173  switch (callback->serverVersion)
174  {
175  case RDPLOCATION_PROTOCOL_VERSION_200:
176  callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
177  break;
178  case RDPLOCATION_PROTOCOL_VERSION_100:
179  callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
180  break;
181  default:
182  callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
183  if (callback->serverVersion > RDPLOCATION_PROTOCOL_VERSION_200)
184  callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
185  break;
186  }
187 
188  char cbuffer[64] = { 0 };
189  char sbuffer[64] = { 0 };
190  WLog_Print(plugin->baseDynPlugin.log, WLOG_DEBUG,
191  "Server version %s, client version %s",
192  location_version_str(callback->serverVersion, sbuffer, sizeof(sbuffer)),
193  location_version_str(callback->clientVersion, cbuffer, sizeof(cbuffer)));
194 
195  if (!plugin->context.LocationStart)
196  {
197  WLog_Print(plugin->baseDynPlugin.log, WLOG_WARN,
198  "LocationStart=NULL, no location data will be sent");
199  return CHANNEL_RC_OK;
200  }
201  const UINT res =
202  plugin->context.LocationStart(&plugin->context, callback->clientVersion, 0);
203  if (res != CHANNEL_RC_OK)
204  return res;
205  return location_send_client_ready_pdu(callback);
206  default:
207  WLog_WARN(TAG, "invalid pduType=%s");
208  return ERROR_INVALID_DATA;
209  }
210 }
211 
212 static UINT location_send_base_location3d(IWTSVirtualChannel* channel,
214 {
215  wStream sbuffer = { 0 };
216  BYTE buffer[32] = { 0 };
217  wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
218  WINPR_ASSERT(s);
219  WINPR_ASSERT(channel);
220  WINPR_ASSERT(pdu);
221 
222  if (pdu->source)
223  WLog_DBG(TAG,
224  "latitude=%lf, logitude=%lf, altitude=%" PRId32
225  ", speed=%lf, heading=%lf, haccuracy=%lf, source=%" PRIu8,
226  pdu->latitude, pdu->longitude, pdu->altitude, pdu->speed, pdu->heading,
227  pdu->horizontalAccuracy, *pdu->source);
228  else
229  WLog_DBG(TAG, "latitude=%lf, logitude=%lf, altitude=%" PRId32, pdu->latitude,
230  pdu->longitude, pdu->altitude);
231 
232  if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
233  return ERROR_OUTOFMEMORY;
234 
235  if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
236  !freerdp_write_four_byte_float(s, pdu->longitude) ||
237  !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
238  return ERROR_INTERNAL_ERROR;
239 
240  if (pdu->source)
241  {
242  if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
243  !freerdp_write_four_byte_float(s, *pdu->heading) ||
244  !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
245  return ERROR_INTERNAL_ERROR;
246 
247  Stream_Write_UINT8(s, *pdu->source);
248  }
249 
250  return location_channel_send(channel, s);
251 }
252 
253 static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
255 {
256  wStream sbuffer = { 0 };
257  BYTE buffer[32] = { 0 };
258  wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
259  WINPR_ASSERT(s);
260 
261  WINPR_ASSERT(channel);
262  WINPR_ASSERT(pdu);
263 
264  const BOOL ext = pdu->speedDelta && pdu->headingDelta;
265 
266  if (ext)
267  WLog_DBG(TAG, "latitude=%lf, logitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
268  pdu->longitudeDelta, pdu->speedDelta, pdu->headingDelta);
269  else
270  WLog_DBG(TAG, "latitude=%lf, logitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
271 
272  if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
273  return ERROR_OUTOFMEMORY;
274 
275  if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
276  !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
277  return ERROR_INTERNAL_ERROR;
278 
279  if (ext)
280  {
281  if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
282  !freerdp_write_four_byte_float(s, *pdu->headingDelta))
283  return ERROR_INTERNAL_ERROR;
284  }
285 
286  return location_channel_send(channel, s);
287 }
288 
289 static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
291 {
292  wStream sbuffer = { 0 };
293  BYTE buffer[32] = { 0 };
294  wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
295  WINPR_ASSERT(s);
296 
297  WINPR_ASSERT(channel);
298  WINPR_ASSERT(pdu);
299 
300  const BOOL ext = pdu->speedDelta && pdu->headingDelta;
301 
302  if (ext)
303  WLog_DBG(TAG, "latitude=%lf, logitude=%lf, altitude=%" PRId32 ", speed=%lf, heading=%lf",
304  pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta, pdu->speedDelta,
305  pdu->headingDelta);
306  else
307  WLog_DBG(TAG, "latitude=%lf, logitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
308  pdu->longitudeDelta, pdu->altitudeDelta);
309 
310  if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
311  return ERROR_OUTOFMEMORY;
312 
313  if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
314  !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
315  !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
316  return ERROR_INTERNAL_ERROR;
317 
318  if (ext)
319  {
320  if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
321  !freerdp_write_four_byte_float(s, *pdu->headingDelta))
322  return ERROR_INTERNAL_ERROR;
323  }
324 
325  return location_channel_send(channel, s);
326 }
327 
328 static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type, size_t count, ...)
329 {
330  WINPR_ASSERT(context);
331 
332  LOCATION_PLUGIN* loc = context->handle;
333  WINPR_ASSERT(loc);
334 
335  GENERIC_LISTENER_CALLBACK* cb = loc->baseDynPlugin.listener_callback;
336  WINPR_ASSERT(cb);
337 
338  IWTSVirtualChannel* channel = cb->channel;
339  WINPR_ASSERT(channel);
340 
341  const LOCATION_CALLBACK* callback =
342  (const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
343  WINPR_ASSERT(callback);
344 
345  UINT32 res = ERROR_INTERNAL_ERROR;
346  va_list ap = { 0 };
347  va_start(ap, count);
348  switch (type)
349  {
350  case PDUTYPE_BASE_LOCATION3D:
351  if ((count != 3) && (count != 7))
352  res = ERROR_INVALID_PARAMETER;
353  else
354  {
356  LOCATIONSOURCE source = LOCATIONSOURCE_IP;
357  double speed = FP_NAN;
358  double heading = FP_NAN;
359  double horizontalAccuracy = FP_NAN;
360  pdu.latitude = va_arg(ap, double);
361  pdu.longitude = va_arg(ap, double);
362  pdu.altitude = va_arg(ap, INT32);
363  if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
364  {
365  speed = va_arg(ap, double);
366  heading = va_arg(ap, double);
367  horizontalAccuracy = va_arg(ap, double);
368  source = va_arg(ap, int);
369  pdu.speed = &speed;
370  pdu.heading = &heading;
371  pdu.horizontalAccuracy = &horizontalAccuracy;
372  pdu.source = &source;
373  }
374  res = location_send_base_location3d(channel, &pdu);
375  }
376  break;
377  case PDUTYPE_LOCATION2D_DELTA:
378  if ((count != 2) && (count != 4))
379  res = ERROR_INVALID_PARAMETER;
380  else
381  {
383 
384  pdu.latitudeDelta = va_arg(ap, double);
385  pdu.longitudeDelta = va_arg(ap, double);
386 
387  double speedDelta = FP_NAN;
388  double headingDelta = FP_NAN;
389  if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
390  {
391  speedDelta = va_arg(ap, double);
392  headingDelta = va_arg(ap, double);
393  pdu.speedDelta = &speedDelta;
394  pdu.headingDelta = &headingDelta;
395  }
396  res = location_send_location2d_delta(channel, &pdu);
397  }
398  break;
399  case PDUTYPE_LOCATION3D_DELTA:
400  if ((count != 3) && (count != 5))
401  res = ERROR_INVALID_PARAMETER;
402  else
403  {
405  double speedDelta = FP_NAN;
406  double headingDelta = FP_NAN;
407 
408  pdu.latitudeDelta = va_arg(ap, double);
409  pdu.longitudeDelta = va_arg(ap, double);
410  pdu.altitudeDelta = va_arg(ap, INT32);
411  if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
412  {
413  speedDelta = va_arg(ap, double);
414  headingDelta = va_arg(ap, double);
415  pdu.speedDelta = &speedDelta;
416  pdu.headingDelta = &headingDelta;
417  }
418  res = location_send_location3d_delta(channel, &pdu);
419  }
420  break;
421  default:
422  res = ERROR_INVALID_PARAMETER;
423  break;
424  }
425  va_end(ap);
426  return res;
427 }
428 
434 static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
435 {
436  UINT res = CHANNEL_RC_OK;
437  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
438 
439  if (callback)
440  {
441  LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
442  WINPR_ASSERT(plugin);
443 
444  res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
445  }
446  free(callback);
447 
448  return res;
449 }
450 
451 static UINT location_init(GENERIC_DYNVC_PLUGIN* plugin, rdpContext* context, rdpSettings* settings)
452 {
453  LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
454 
455  WINPR_ASSERT(loc);
456 
457  loc->context.LocationSend = location_send;
458  loc->context.handle = loc;
459  plugin->iface.pInterface = &loc->context;
460  return CHANNEL_RC_OK;
461 }
462 
463 static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
464  NULL, /* Open */
465  location_on_close, NULL };
466 
472 FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
473 {
474  return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
475  sizeof(LOCATION_PLUGIN), sizeof(LOCATION_CALLBACK),
476  &location_callbacks, location_init, NULL);
477 }