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