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