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 if (!Stream_SetPosition(s, 2))
107 return ERROR_INVALID_DATA;
108 Stream_Write_UINT32(s, (UINT32)len);
109
110 WINPR_ASSERT(channel);
111 WINPR_ASSERT(channel->Write);
112 return channel->Write(channel, (UINT32)len, Stream_Buffer(s), nullptr);
113}
114
115static UINT location_send_client_ready_pdu(const LOCATION_CALLBACK* callback)
116{
117 wStream sbuffer = WINPR_C_ARRAY_INIT;
118 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
119 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
120 WINPR_ASSERT(s);
121
122 if (!location_write_header(s, PDUTYPE_CLIENT_READY, 8))
123 return ERROR_OUTOFMEMORY;
124
125 Stream_Write_UINT32(s, callback->clientVersion);
126 Stream_Write_UINT32(s, callback->clientFlags);
127 return location_channel_send(callback->baseCb.channel, s);
128}
129
130static const char* location_version_str(UINT32 version, char* buffer, size_t size)
131{
132 const char* str = nullptr;
133 switch (version)
134 {
135 case RDPLOCATION_PROTOCOL_VERSION_100:
136 str = "RDPLOCATION_PROTOCOL_VERSION_100";
137 break;
138 case RDPLOCATION_PROTOCOL_VERSION_200:
139 str = "RDPLOCATION_PROTOCOL_VERSION_200";
140 break;
141 default:
142 str = "RDPLOCATION_PROTOCOL_VERSION_UNKNOWN";
143 break;
144 }
145
146 (void)_snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, version);
147 return buffer;
148}
149
155static UINT location_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
156{
157 LOCATION_CALLBACK* callback = (LOCATION_CALLBACK*)pChannelCallback;
158
159 WINPR_ASSERT(callback);
160
161 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->baseCb.plugin;
162 WINPR_ASSERT(plugin);
163
164 UINT16 pduType = 0;
165 UINT32 pduLength = 0;
166 if (!location_read_header(plugin->baseDynPlugin.log, data, &pduType, &pduLength))
167 return ERROR_INVALID_DATA;
168
169 switch (pduType)
170 {
171 case PDUTYPE_SERVER_READY:
172 if (!location_read_server_ready_pdu(callback, data, pduLength))
173 return ERROR_INVALID_DATA;
174
175 switch (callback->serverVersion)
176 {
177 case RDPLOCATION_PROTOCOL_VERSION_200:
178 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
179 break;
180 case RDPLOCATION_PROTOCOL_VERSION_100:
181 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
182 break;
183 default:
184 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
185 if (callback->serverVersion > RDPLOCATION_PROTOCOL_VERSION_200)
186 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
187 break;
188 }
189
190 {
191 char cbuffer[64] = WINPR_C_ARRAY_INIT;
192 char sbuffer[64] = WINPR_C_ARRAY_INIT;
193 WLog_Print(plugin->baseDynPlugin.log, WLOG_DEBUG,
194 "Server version %s, client version %s",
195 location_version_str(callback->serverVersion, sbuffer, sizeof(sbuffer)),
196 location_version_str(callback->clientVersion, cbuffer, sizeof(cbuffer)));
197 }
198
199 if (!plugin->context.LocationStart)
200 {
201 WLog_Print(plugin->baseDynPlugin.log, WLOG_WARN,
202 "LocationStart=nullptr, no location data will be sent");
203 return CHANNEL_RC_OK;
204 }
205
206 {
207 const UINT res =
208 plugin->context.LocationStart(&plugin->context, callback->clientVersion, 0);
209 if (res != CHANNEL_RC_OK)
210 return res;
211 }
212 return location_send_client_ready_pdu(callback);
213 default:
214 WLog_WARN(TAG, "invalid pduType=%" PRIu16, pduType);
215 return ERROR_INVALID_DATA;
216 }
217}
218
219static UINT location_send_base_location3d(IWTSVirtualChannel* channel,
221{
222 wStream sbuffer = WINPR_C_ARRAY_INIT;
223 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
224 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
225 WINPR_ASSERT(s);
226 WINPR_ASSERT(channel);
227 WINPR_ASSERT(pdu);
228
229 if (pdu->source)
230 WLog_DBG(TAG,
231 "latitude=%lf, longitude=%lf, altitude=%" PRId32
232 ", speed=%lf, heading=%lf, haccuracy=%lf, source=%" PRIu8,
233 pdu->latitude, pdu->longitude, pdu->altitude, pdu->speed ? *pdu->speed : FP_NAN,
234 pdu->heading ? *pdu->heading : FP_NAN,
235 pdu->horizontalAccuracy ? *pdu->horizontalAccuracy : FP_NAN, *pdu->source);
236 else
237 WLog_DBG(TAG, "latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitude,
238 pdu->longitude, pdu->altitude);
239
240 if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
241 return ERROR_OUTOFMEMORY;
242
243 if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
244 !freerdp_write_four_byte_float(s, pdu->longitude) ||
245 !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
246 return ERROR_INTERNAL_ERROR;
247
248 if (pdu->source)
249 {
250 if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
251 !freerdp_write_four_byte_float(s, *pdu->heading) ||
252 !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
253 return ERROR_INTERNAL_ERROR;
254
255 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, *pdu->source));
256 }
257
258 return location_channel_send(channel, s);
259}
260
261static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
263{
264 wStream sbuffer = WINPR_C_ARRAY_INIT;
265 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
266 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
267 WINPR_ASSERT(s);
268
269 WINPR_ASSERT(channel);
270 WINPR_ASSERT(pdu);
271
272 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
273
274 if (ext)
275 WLog_DBG(TAG, "latitude=%lf, longitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
276 pdu->longitudeDelta, pdu->speedDelta ? *pdu->speedDelta : FP_NAN,
277 pdu->headingDelta ? *pdu->headingDelta : FP_NAN);
278 else
279 WLog_DBG(TAG, "latitude=%lf, longitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
280
281 if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
282 return ERROR_OUTOFMEMORY;
283
284 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
285 !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
286 return ERROR_INTERNAL_ERROR;
287
288 if (ext)
289 {
290 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
291 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
292 return ERROR_INTERNAL_ERROR;
293 }
294
295 return location_channel_send(channel, s);
296}
297
298static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
300{
301 wStream sbuffer = WINPR_C_ARRAY_INIT;
302 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
303 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
304 WINPR_ASSERT(s);
305
306 WINPR_ASSERT(channel);
307 WINPR_ASSERT(pdu);
308
309 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
310
311 if (ext)
312 WLog_DBG(TAG, "latitude=%lf, longitude=%lf, altitude=%" PRId32 ", speed=%lf, heading=%lf",
313 pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta,
314 pdu->speedDelta ? *pdu->speedDelta : FP_NAN,
315 pdu->headingDelta ? *pdu->headingDelta : FP_NAN);
316 else
317 WLog_DBG(TAG, "latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
318 pdu->longitudeDelta, pdu->altitudeDelta);
319
320 if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
321 return ERROR_OUTOFMEMORY;
322
323 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
324 !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
325 !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
326 return ERROR_INTERNAL_ERROR;
327
328 if (ext)
329 {
330 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
331 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
332 return ERROR_INTERNAL_ERROR;
333 }
334
335 return location_channel_send(channel, s);
336}
337
338static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type, size_t count, ...)
339{
340 WINPR_ASSERT(context);
341
342 LOCATION_PLUGIN* loc = context->handle;
343 WINPR_ASSERT(loc);
344
345 GENERIC_LISTENER_CALLBACK* cb = loc->baseDynPlugin.listener_callback;
346 WINPR_ASSERT(cb);
347
348 IWTSVirtualChannel* channel = cb->channel;
349 WINPR_ASSERT(channel);
350
351 const LOCATION_CALLBACK* callback =
352 (const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
353 WINPR_ASSERT(callback);
354
355 UINT32 res = ERROR_INTERNAL_ERROR;
356 va_list ap = WINPR_C_ARRAY_INIT;
357 va_start(ap, count);
358 switch (type)
359 {
360 case PDUTYPE_BASE_LOCATION3D:
361 if ((count != 3) && (count != 7))
362 res = ERROR_INVALID_PARAMETER;
363 else
364 {
365 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
366 double speed = FP_NAN;
367 double heading = FP_NAN;
368 double horizontalAccuracy = FP_NAN;
369 RDPLOCATION_BASE_LOCATION3D_PDU pdu = { .latitude = va_arg(ap, double),
370 .longitude = va_arg(ap, double),
371 .altitude = va_arg(ap, INT32),
372 .speed = nullptr,
373 .heading = nullptr,
374 .horizontalAccuracy = nullptr,
375 .source = nullptr };
376
377 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
378 {
379 speed = va_arg(ap, double);
380 heading = va_arg(ap, double);
381 horizontalAccuracy = va_arg(ap, double);
382 source = WINPR_ASSERTING_INT_CAST(LOCATIONSOURCE, va_arg(ap, int));
383 pdu.speed = &speed;
384 pdu.heading = &heading;
385 pdu.horizontalAccuracy = &horizontalAccuracy;
386 pdu.source = &source;
387 }
388 res = location_send_base_location3d(channel, &pdu);
389 }
390 break;
391 case PDUTYPE_LOCATION2D_DELTA:
392 if ((count != 2) && (count != 4))
393 res = ERROR_INVALID_PARAMETER;
394 else
395 {
396 RDPLOCATION_LOCATION2D_DELTA_PDU pdu = { .latitudeDelta = va_arg(ap, double),
397 .longitudeDelta = va_arg(ap, double),
398 .speedDelta = nullptr,
399 .headingDelta = nullptr };
400
401 double speedDelta = FP_NAN;
402 double headingDelta = FP_NAN;
403 if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
404 {
405 speedDelta = va_arg(ap, double);
406 headingDelta = va_arg(ap, double);
407 pdu.speedDelta = &speedDelta;
408 pdu.headingDelta = &headingDelta;
409 }
410 res = location_send_location2d_delta(channel, &pdu);
411 }
412 break;
413 case PDUTYPE_LOCATION3D_DELTA:
414 if ((count != 3) && (count != 5))
415 res = ERROR_INVALID_PARAMETER;
416 else
417 {
418 double speedDelta = FP_NAN;
419 double headingDelta = FP_NAN;
420
421 RDPLOCATION_LOCATION3D_DELTA_PDU pdu = { .latitudeDelta = va_arg(ap, double),
422 .longitudeDelta = va_arg(ap, double),
423 .altitudeDelta = va_arg(ap, INT32),
424 .speedDelta = nullptr,
425 .headingDelta = nullptr };
426 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
427 {
428 speedDelta = va_arg(ap, double);
429 headingDelta = va_arg(ap, double);
430 pdu.speedDelta = &speedDelta;
431 pdu.headingDelta = &headingDelta;
432 }
433 res = location_send_location3d_delta(channel, &pdu);
434 }
435 break;
436 default:
437 res = ERROR_INVALID_PARAMETER;
438 break;
439 }
440 va_end(ap);
441 return res;
442}
443
449static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
450{
451 UINT res = CHANNEL_RC_OK;
452 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
453
454 if (callback)
455 {
456 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
457 WINPR_ASSERT(plugin);
458
459 res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
460 }
461 free(callback);
462
463 return res;
464}
465
466static UINT location_init(GENERIC_DYNVC_PLUGIN* plugin, WINPR_ATTR_UNUSED rdpContext* context,
467 WINPR_ATTR_UNUSED rdpSettings* settings)
468{
469 LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
470
471 WINPR_ASSERT(loc);
472
473 loc->context.LocationSend = location_send;
474 loc->context.handle = loc;
475 plugin->iface.pInterface = &loc->context;
476 return CHANNEL_RC_OK;
477}
478
479static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
480 nullptr, /* Open */
481 location_on_close, nullptr };
482
488FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
489{
490 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
491 sizeof(LOCATION_PLUGIN), sizeof(LOCATION_CALLBACK),
492 &location_callbacks, location_init, nullptr);
493}