FreeRDP
Loading...
Searching...
No Matches
multitransport.c
1
20#include <winpr/assert.h>
21#include <winpr/collections.h>
22#include <winpr/synch.h>
23#include <winpr/winsock.h>
24#include <freerdp/config.h>
25
26#if !defined(_WIN32)
27#include <netdb.h>
28#include <sys/socket.h>
29#endif
30#include <freerdp/log.h>
31#include <freerdp/multitransport.h>
32#include <freerdp/channels/drdynvc.h>
33
34#include "settings.h"
35#include "rdp.h"
36#include "transport.h"
37#include "rdpemt.h"
38#include "multitransport.h"
39
40typedef enum
41{
42 MT_EVENT_READY,
43 MT_EVENT_FAILED,
44 MT_EVENT_DATA,
45 MT_EVENT_AUTODETECT_REQUEST
46} MT_EVENT_TYPE;
47
48/* Tunnel events travel from the tunnel thread to the thread that runs the main loop, which is
49 * where the Initiate Multitransport Response goes out and where the dynamic channel code is
50 * called. */
51typedef struct
52{
53 MT_EVENT_TYPE type;
54 wStream* s; /* sealed, at position 0 */
55 size_t length;
56} mt_event;
57
58struct rdp_multitransport
59{
60 rdpRdp* rdp;
61
62 MultiTransportRequestCb MtRequest;
63 MultiTransportResponseCb MtResponse;
64
65 /* server-side data */
66 UINT32 reliableReqId;
67
68 BYTE reliableCookie[RDPUDP_COOKIE_LEN];
69 BYTE reliableCookieHash[RDPUDP_COOKIE_HASHLEN];
70
71 /* client-side data */
73 HANDLE event;
74 wQueue* events;
75 rdpEmt* reliable;
76 FreeRDP_MultitransportTunnelState reliableState;
77 BOOL reliableResponseSent;
78
79 /* Written on the thread that runs the main loop, which reads them there without locking:
80 * receiving tunnel data must not wait for a sender, which holds lock while it writes to the
81 * tunnel. The tunnel thread reads them only while it hands data on directly, see
82 * multitransport_direct. */
84 void* custom;
85
86 /* Main loop thread only: the connection was ended because its tunnel broke down. */
87 BOOL endedConnection;
88
89 /* Set by the tunnel thread when it could not queue an event, received data that was
90 * acknowledged to the server already among them. Accessed with Interlocked functions. */
91 LONG tunnelEventLost;
92};
93
94enum
95{
96 RDPTUNNEL_ACTION_CREATEREQUEST = 0x00,
97 RDPTUNNEL_ACTION_CREATERESPONSE = 0x01,
98 RDPTUNNEL_ACTION_DATA = 0x02
99};
100
101#define TAG FREERDP_TAG("core.multitransport")
102
103state_run_t multitransport_recv_request(rdpMultitransport* multi, wStream* s)
104{
105 WINPR_ASSERT(multi);
106 rdpSettings* settings = multi->rdp->settings;
107
108 if (freerdp_settings_get_bool(settings, FreeRDP_ServerMode))
109 {
110 WLog_ERR(TAG, "not expecting a multi-transport request in server mode");
111 return STATE_RUN_FAILED;
112 }
113
114 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
115 return STATE_RUN_FAILED;
116
117 UINT32 requestId = 0;
118 UINT16 requestedProto = 0;
119 UINT16 reserved = 0;
120 const BYTE* cookie = nullptr;
121
122 Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
123 Stream_Read_UINT16(s, requestedProto); /* requestedProtocol (2 bytes) */
124 Stream_Read_UINT16(s, reserved); /* reserved (2 bytes) */
125 cookie = Stream_ConstPointer(s);
126 Stream_Seek(s, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
127 if (reserved != 0)
128 {
129 /*
130 * If the reserved filed is not 0 the request PDU seems to contain some extra data.
131 * If the reserved value is 1, then two bytes of 0 (probably a version field)
132 * are followed by a JSON payload (not null terminated, until the end of the packet.
133 * There seems to be no dedicated length field)
134 *
135 * for now just ignore all that
136 */
137 WLog_WARN(TAG,
138 "reserved is %" PRIu16 " instead of 0, skipping %" PRIuz "bytes of unknown data",
139 reserved, Stream_GetRemainingLength(s));
140 if (!Stream_SafeSeek(s, Stream_GetRemainingLength(s)))
141 return STATE_RUN_FAILED;
142 }
143
144 WINPR_ASSERT(multi->MtRequest);
145 return multi->MtRequest(multi, requestId, requestedProto, cookie);
146}
147
148WINPR_ATTR_NODISCARD
149static BOOL multitransport_request_send(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
150 const BYTE* cookie)
151{
152 WINPR_ASSERT(multi);
153 WINPR_ASSERT(cookie);
154 UINT16 sec_flags = 0;
155 wStream* s = rdp_message_channel_pdu_init(multi->rdp, &sec_flags);
156 if (!s)
157 return FALSE;
158
159 if (!Stream_EnsureRemainingCapacity(s, 24))
160 {
161 Stream_Release(s);
162 return FALSE;
163 }
164
165 Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
166 Stream_Write_UINT16(s, reqProto); /* requestedProtocol (2 bytes) */
167 Stream_Zero(s, 2); /* reserved (2 bytes) */
168 Stream_Write(s, cookie, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
169
170 return rdp_send_message_channel_pdu(multi->rdp, s, sec_flags | SEC_TRANSPORT_REQ);
171}
172
173state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto)
174{
175 WINPR_ASSERT(multi);
176
177 /* TODO: move this static variable to the listener */
178 static UINT32 reqId = 0;
179
180 if (reqProto == INITIATE_REQUEST_PROTOCOL_UDPFECR)
181 {
182 multi->reliableReqId = reqId++;
183 if (winpr_RAND(multi->reliableCookie, sizeof(multi->reliableCookie)) < 0)
184 return STATE_RUN_FAILED;
185
186 return multitransport_request_send(multi, multi->reliableReqId, reqProto,
187 multi->reliableCookie)
188 ? STATE_RUN_SUCCESS
189 : STATE_RUN_FAILED;
190 }
191
192 WLog_ERR(TAG, "only reliable transport is supported");
193 return STATE_RUN_CONTINUE;
194}
195
196BOOL multitransport_client_send_response(rdpMultitransport* multi, UINT32 reqId, HRESULT hr)
197{
198 WINPR_ASSERT(multi);
199
200 UINT16 sec_flags = 0;
201 wStream* s = rdp_message_channel_pdu_init(multi->rdp, &sec_flags);
202 if (!s)
203 return FALSE;
204
205 if (!Stream_EnsureRemainingCapacity(s, 28))
206 {
207 Stream_Release(s);
208 return FALSE;
209 }
210
211 Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
212
213 /* [MS-RDPBCGR] 2.2.15.2 Client Initiate Multitransport Response PDU defines this as 4byte
214 * UNSIGNED but https://learn.microsoft.com/en-us/windows/win32/learnwin32/error-codes-in-com
215 * defines this as signed... assume the spec is (implicitly) assuming twos complement. */
216 Stream_Write_INT32(s, hr); /* HResult (4 bytes) */
217 return rdp_send_message_channel_pdu(multi->rdp, s, sec_flags | SEC_TRANSPORT_RSP);
218}
219
220state_run_t multitransport_recv_response(rdpMultitransport* multi, wStream* s)
221{
222 WINPR_ASSERT(multi && multi->rdp);
223 WINPR_ASSERT(s);
224
225 rdpSettings* settings = multi->rdp->settings;
226 WINPR_ASSERT(settings);
227
228 if (!freerdp_settings_get_bool(settings, FreeRDP_ServerMode))
229 {
230 WLog_ERR(TAG, "client is not expecting a multi-transport resp packet");
231 return STATE_RUN_FAILED;
232 }
233
234 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
235 return STATE_RUN_FAILED;
236
237 UINT32 requestId = 0;
238 UINT32 hr = 0;
239
240 Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
241 Stream_Read_UINT32(s, hr); /* hrResponse (4 bytes) */
242
243 state_run_t res = STATE_RUN_SUCCESS;
244 IFCALLRET(multi->MtResponse, res, multi, requestId, hr);
245 return res;
246}
247
248WINPR_ATTR_NODISCARD
249static BOOL multitransport_soft_sync(const rdpMultitransport* multi)
250{
251 WINPR_ASSERT(multi);
252 WINPR_ASSERT(multi->rdp);
253 const UINT32 flags =
254 freerdp_settings_get_uint32(multi->rdp->settings, FreeRDP_MultitransportFlags);
255 return (flags & SOFTSYNC_TCP_TO_UDP) != 0;
256}
257
258WINPR_ATTR_NODISCARD
259static state_run_t multitransport_decline(rdpMultitransport* multi, UINT32 reqId)
260{
261 WINPR_ASSERT(multi);
262 return multitransport_client_send_response(multi, reqId, E_ABORT) ? STATE_RUN_SUCCESS
263 : STATE_RUN_FAILED;
264}
265
266static void mt_event_free(void* obj)
267{
268 mt_event* ev = obj;
269 if (!ev)
270 return;
271 if (ev->s)
272 Stream_Release(ev->s);
273 free(ev);
274}
275
276/* Runs on the tunnel thread, once multitransport_direct has let it. */
277WINPR_ATTR_NODISCARD
278static BOOL multitransport_direct_data(void* custom, wStream* s)
279{
280 rdpMultitransport* multi = custom;
281 WINPR_ASSERT(multi);
282 return multi->callbacks.TunnelDataReceived(multi->custom, TUNNELTYPE_UDPFECR, s);
283}
284
285/* Runs on the tunnel thread. */
286static void multitransport_tunnel_event(void* custom, rdpEmt* emt, RDPEMT_EVENT event,
287 const BYTE* data, size_t length)
288{
289 rdpMultitransport* multi = custom;
290 WINPR_ASSERT(multi);
291 WINPR_UNUSED(emt);
292
293 mt_event* ev = calloc(1, sizeof(mt_event));
294 if (!ev)
295 goto fail;
296
297 switch (event)
298 {
299 case RDPEMT_EVENT_READY:
300 ev->type = MT_EVENT_READY;
301 break;
302 case RDPEMT_EVENT_FAILED:
303 ev->type = MT_EVENT_FAILED;
304 break;
305 case RDPEMT_EVENT_AUTODETECT_REQUEST:
306 case RDPEMT_EVENT_DATA:
307 default:
308 ev->type = (event == RDPEMT_EVENT_AUTODETECT_REQUEST) ? MT_EVENT_AUTODETECT_REQUEST
309 : MT_EVENT_DATA;
310 if (length == 0)
311 break; /* nothing to hand up, malloc(0) may return nullptr */
312 WINPR_ASSERT(data);
313 /* the receiver takes this stream over, the data is not copied again on the way */
314 ev->s = Stream_New(nullptr, length);
315 if (!ev->s)
316 goto fail;
317 Stream_Write(ev->s, data, length);
318 Stream_SealLength(ev->s);
319 Stream_ResetPosition(ev->s);
320 ev->length = length;
321 break;
322 }
323
324 if (!Queue_Enqueue(multi->events, ev))
325 goto fail;
326 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): Queue_Enqueue takes ownership of ev
327 (void)SetEvent(multi->event);
328 return;
329
330fail:
331 /* A lost event leaves the tunnel in an unknown state, the main loop ends the connection. */
332 mt_event_free(ev);
333 /* compare-exchange: WinPR's InterlockedExchange reads the old value non-atomically */
334 if (InterlockedCompareExchange(&multi->tunnelEventLost, 1, 0) == 0)
335 WLog_ERR(TAG, "out of memory, a UDP tunnel event was lost");
336 (void)SetEvent(multi->event);
337}
338
339/* The UDP listener sits on the same address and port as the TCP one. */
340WINPR_ATTR_NODISCARD
341static BOOL multitransport_resolve_server(const rdpSettings* settings,
342 struct sockaddr_storage* address, size_t* length)
343{
344 WINPR_ASSERT(settings);
345 WINPR_ASSERT(address);
346 WINPR_ASSERT(length);
347
348 const char* hostname = freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
349 if (!hostname)
350 return FALSE;
351
352 char port[16] = WINPR_C_ARRAY_INIT;
353 (void)_snprintf(port, sizeof(port), "%" PRIu32,
354 freerdp_settings_get_uint32(settings, FreeRDP_ServerPort));
355
356 struct addrinfo hints = WINPR_C_ARRAY_INIT;
357 hints.ai_family = AF_UNSPEC;
358 hints.ai_socktype = SOCK_DGRAM;
359 struct addrinfo* result = nullptr;
360 if ((getaddrinfo(hostname, port, &hints, &result) != 0) || !result)
361 return FALSE;
362
363 BOOL rc = FALSE;
364 if (result->ai_addrlen <= sizeof(*address))
365 {
366 memcpy(address, result->ai_addr, result->ai_addrlen);
367 *length = result->ai_addrlen;
368 rc = TRUE;
369 }
370 freeaddrinfo(result);
371 return rc;
372}
373
374WINPR_ATTR_NODISCARD
375static state_run_t multitransport_client_request(rdpMultitransport* multi, UINT32 reqId,
376 UINT16 reqProto, const BYTE* cookie)
377{
378 WINPR_ASSERT(multi);
379 rdpRdp* rdp = multi->rdp;
380 WINPR_ASSERT(rdp);
381 rdpSettings* settings = rdp->settings;
382 WINPR_ASSERT(settings);
383
384 WLog_DBG(TAG, "Initiate Multitransport Request: requestId %" PRIu32 ", protocol 0x%04" PRIx16,
385 reqId, reqProto);
386
387 if (reqProto != INITIATE_REQUEST_PROTOCOL_UDPFECR)
388 {
389 WLog_INFO(TAG, "declining lossy UDP, only the reliable transport is implemented");
390 return multitransport_decline(multi, reqId);
391 }
392
393 if (!rdpemt_is_supported())
394 {
395 WLog_DBG(TAG, "declining UDP, built without -DWITH_RDPEUDP=ON");
396 return multitransport_decline(multi, reqId);
397 }
398
399 if (!freerdp_settings_get_bool(settings, FreeRDP_SupportMultitransport) ||
400 !(freerdp_settings_get_uint32(settings, FreeRDP_MultitransportFlags) &
401 TRANSPORT_TYPE_UDP_FECR))
402 return multitransport_decline(multi, reqId);
403
404 EnterCriticalSection(&multi->lock);
405 const BOOL busy = (multi->reliable != nullptr);
406 LeaveCriticalSection(&multi->lock);
407 if (busy)
408 {
409 WLog_WARN(TAG, "a reliable UDP tunnel already exists, declining request %" PRIu32, reqId);
410 return multitransport_decline(multi, reqId);
411 }
412
413 /* The tunnel's TLS session has to end at the server the main connection was verified
414 * against, which needs the main connection to be TLS directly to that server. */
415 const BYTE* publicKey = nullptr;
416 DWORD publicKeyLength = 0;
417 if (!transport_get_public_key(rdp->transport, &publicKey, &publicKeyLength) || !publicKey ||
418 (publicKeyLength == 0))
419 {
420 WLog_INFO(TAG, "declining UDP, the main connection does not use TLS");
421 return multitransport_decline(multi, reqId);
422 }
423
424 /* The transport says whether the gateway is used: with /gateway:usage-method:detect the
425 * connection can go to the server directly while FreeRDP_GatewayEnabled stays set. */
426 const TRANSPORT_LAYER layer = transport_get_layer(rdp->transport);
427 if ((layer == TRANSPORT_LAYER_TSG) || (layer == TRANSPORT_LAYER_TSG_TLS))
428 {
429 WLog_INFO(TAG, "declining UDP, the main connection goes through a gateway");
430 return multitransport_decline(multi, reqId);
431 }
432 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
433 {
434 /* the TCP peer is the proxy, the server's UDP port is not reachable through it */
435 WLog_INFO(TAG, "declining UDP, the main connection goes through a proxy");
436 return multitransport_decline(multi, reqId);
437 }
438
439 struct sockaddr_storage address = WINPR_C_ARRAY_INIT;
440 size_t addressLength = 0;
441 if (!transport_get_peer_address(rdp->transport, &address, &addressLength) &&
442 !multitransport_resolve_server(settings, &address, &addressLength))
443 {
444 WLog_INFO(TAG, "declining UDP, the server address is unknown");
445 return multitransport_decline(multi, reqId);
446 }
447
448 rdpEmt* emt = rdpemt_new(WLog_Get(TAG), reqId, cookie,
449 freerdp_settings_get_string(settings, FreeRDP_ServerHostname),
450 publicKey, publicKeyLength, multitransport_tunnel_event, multi);
451 if (!emt)
452 return multitransport_decline(multi, reqId);
453
454 EnterCriticalSection(&multi->lock);
455 multi->reliable = emt;
456 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_PENDING;
457 multi->reliableResponseSent = FALSE;
458 LeaveCriticalSection(&multi->lock);
459
460 if (!rdpemt_start(emt, (const struct sockaddr*)&address, addressLength))
461 {
462 EnterCriticalSection(&multi->lock);
463 multi->reliable = nullptr;
464 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
465 LeaveCriticalSection(&multi->lock);
466 rdpemt_free(emt);
467 return multitransport_decline(multi, reqId);
468 }
469
470 /* The response goes out once the tunnel is up, or has failed. Answering before the server
471 * has accepted the tunnel makes it drop the session. */
472 WLog_INFO(TAG, "setting up a reliable UDP tunnel for request %" PRIu32, reqId);
473 return STATE_RUN_SUCCESS;
474}
475
476/* A failed callback ends the connection like a broken main transport. */
477WINPR_ATTR_NODISCARD
478static BOOL multitransport_callback_failed(rdpMultitransport* multi, const char* what)
479{
480 WINPR_ASSERT(multi);
481 WINPR_ASSERT(multi->rdp);
482 WINPR_ASSERT(what);
483
484 WLog_ERR(TAG, "the dynamic channel code failed to handle %s, ending the connection", what);
485 freerdp_set_last_error_if_not(multi->rdp->context, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
486 return FALSE;
487}
488
489WINPR_ATTR_NODISCARD
490static BOOL multitransport_notify_state(rdpMultitransport* multi,
491 FreeRDP_MultitransportTunnelState state)
492{
493 WINPR_ASSERT(multi);
494
495 if (!multi->callbacks.TunnelStateChanged)
496 return TRUE;
497 if (!multi->callbacks.TunnelStateChanged(multi->custom, TUNNELTYPE_UDPFECR, state))
498 return multitransport_callback_failed(multi, "a tunnel state change");
499 return TRUE;
500}
501
502WINPR_ATTR_NODISCARD
503static BOOL multitransport_deliver_data(rdpMultitransport* multi, mt_event* ev)
504{
505 WINPR_ASSERT(multi);
506 WINPR_ASSERT(multi->rdp);
507 WINPR_ASSERT(ev);
508
509 /* [MS-RDPBCGR] 3.2.5.17: a bandwidth measurement counts the data after the tunnel header */
510 rdpAutoDetect* autodetect = multi->rdp->autodetect;
511 if (autodetect && autodetect->bandwidthMeasureStarted)
512 autodetect->bandwidthMeasureByteCount += WINPR_ASSERTING_INT_CAST(UINT32, ev->length);
513
514 if (!multi->callbacks.TunnelDataReceived || !ev->s)
515 {
516 WLog_DBG(TAG, "dropping %" PRIuz " bytes of tunnel data, no receiver", ev->length);
517 return TRUE;
518 }
519 /* The data was acknowledged to the server, losing it leaves a hole in the channel stream. */
520 wStream* s = ev->s;
521 ev->s = nullptr; /* the callback owns it now */
522 if (!multi->callbacks.TunnelDataReceived(multi->custom, TUNNELTYPE_UDPFECR, s))
523 return multitransport_callback_failed(multi, "tunnel data");
524 return TRUE;
525}
526
527WINPR_ATTR_NODISCARD
528static BOOL multitransport_deliver_autodetect(rdpMultitransport* multi, const mt_event* ev)
529{
530 WINPR_ASSERT(multi);
531 WINPR_ASSERT(multi->rdp);
532 WINPR_ASSERT(ev);
533
534 if (!ev->s)
535 return TRUE;
536 return !state_run_failed(
537 autodetect_recv_tunnel_request(multi->rdp->autodetect, RDP_TRANSPORT_UDP_R, ev->s));
538}
539
540WINPR_ATTR_NODISCARD
541static BOOL multitransport_handle_event(rdpMultitransport* multi, mt_event* ev)
542{
543 WINPR_ASSERT(multi);
544 WINPR_ASSERT(ev);
545
546 switch (ev->type)
547 {
548 case MT_EVENT_READY:
549 {
550 EnterCriticalSection(&multi->lock);
551 const UINT32 reqId = multi->reliable ? rdpemt_get_request_id(multi->reliable) : 0;
552 const BOOL haveTunnel = multi->reliable != nullptr;
553 LeaveCriticalSection(&multi->lock);
554 if (!haveTunnel)
555 return TRUE;
556
557 /* [MS-RDPBCGR] 2.2.15.2: S_OK only goes to a server that does Soft-Sync. Without
558 * it, success is not reported at all. */
559 if (multitransport_soft_sync(multi))
560 {
561 if (!multitransport_client_send_response(multi, reqId, S_OK))
562 return FALSE;
563 }
564
565 EnterCriticalSection(&multi->lock);
566 multi->reliableResponseSent = TRUE;
567 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_READY;
568 LeaveCriticalSection(&multi->lock);
569 if (!multitransport_notify_state(multi, FREERDP_MULTITRANSPORT_TUNNEL_READY))
570 return FALSE;
571 }
572 break;
573
574 case MT_EVENT_FAILED:
575 {
576 EnterCriticalSection(&multi->lock);
577 rdpEmt* emt = multi->reliable;
578 const BOOL responseSent = multi->reliableResponseSent;
579 const BOOL wasReady = multi->reliableState == FREERDP_MULTITRANSPORT_TUNNEL_READY;
580 multi->reliable = nullptr;
581 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
582 LeaveCriticalSection(&multi->lock);
583 if (!emt)
584 return TRUE;
585
586 const UINT32 reqId = rdpemt_get_request_id(emt);
587 rdpemt_free(emt);
588
589 /* Soft-Sync only moves channels from TCP to UDP ([MS-RDPEDYC] 3.1.5.3), there is no
590 * way back. What was in flight on the tunnel is lost in both directions, so the
591 * channels on it cannot carry on over TCP: the connection ends like a broken main
592 * transport, and the client reconnects. */
593 if (wasReady)
594 {
595 WLog_WARN(TAG, "the UDP tunnel broke down, ending the connection");
596 multi->endedConnection = TRUE;
597 freerdp_set_last_error_if_not(multi->rdp->context,
598 FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
599 return FALSE;
600 }
601 if (!responseSent)
602 {
603 if (!multitransport_client_send_response(multi, reqId, E_ABORT))
604 return FALSE;
605 }
606 if (!multitransport_notify_state(multi, FREERDP_MULTITRANSPORT_TUNNEL_NONE))
607 return FALSE;
608 }
609 break;
610
611 case MT_EVENT_AUTODETECT_REQUEST:
612 return multitransport_deliver_autodetect(multi, ev);
613
614 case MT_EVENT_DATA:
615 default:
616 return multitransport_deliver_data(multi, ev);
617 }
618 return TRUE;
619}
620
621/* Main loop thread. Once the channels know the tunnel is ready and no data waits in events, a
622 * channel plugin with its own thread gets tunnel data on the tunnel thread, without the way
623 * through the main loop. */
624static void multitransport_direct(rdpMultitransport* multi)
625{
626 WINPR_ASSERT(multi);
627
628 /* reliable and reliableState change on this thread only */
629 if (!multi->reliable || (multi->reliableState != FREERDP_MULTITRANSPORT_TUNNEL_READY) ||
630 !multi->callbacks.DataOnTunnelThread || !multi->callbacks.TunnelDataReceived)
631 return;
632 if (!rdpemt_set_receiver(multi->reliable, multitransport_direct_data, multi, multi->events))
633 WLog_DBG(TAG, "tunnel data still waits in events, the main loop comes back for it");
634}
635
636BOOL multitransport_check(rdpMultitransport* multi)
637{
638 WINPR_ASSERT(multi);
639
640 (void)ResetEvent(multi->event);
641 if (InterlockedCompareExchange(&multi->tunnelEventLost, 0, 0) != 0)
642 {
643 WLog_ERR(TAG, "out of memory queueing UDP tunnel events, ending the connection");
644 freerdp_set_last_error_if_not(multi->rdp->context, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
645 return FALSE;
646 }
647#if defined(WITH_RDPEUDP)
648 /* [MS-RDPBCGR] 3.2.5.17: tunnel data that went to the channels directly counts as the main
649 * loop gets here, like the data it hands on below */
650 if (multi->reliable)
651 {
652 const UINT32 direct = rdpemt_take_direct_bytes(multi->reliable);
653 rdpAutoDetect* autodetect = multi->rdp->autodetect;
654 if (autodetect && autodetect->bandwidthMeasureStarted)
655 autodetect->bandwidthMeasureByteCount += direct;
656 }
657#endif
658
659 BOOL handled = FALSE;
660 while (Queue_Count(multi->events) > 0)
661 {
662 mt_event* ev = Queue_Dequeue(multi->events);
663 if (!ev)
664 break;
665 const BOOL rc = multitransport_handle_event(multi, ev);
666 mt_event_free(ev);
667 if (!rc)
668 return FALSE;
669 handled = TRUE;
670 }
671 if (handled)
672 multitransport_direct(multi);
673 return TRUE;
674}
675
676HANDLE multitransport_get_event_handle(rdpMultitransport* multi)
677{
678 WINPR_ASSERT(multi);
679 return multi->event;
680}
681
682BOOL multitransport_send_autodetect_response(rdpMultitransport* multi, const BYTE* data,
683 size_t length)
684{
685 WINPR_ASSERT(multi);
686
687 BOOL rc = FALSE;
688 EnterCriticalSection(&multi->lock);
689 if (multi->reliable && (multi->reliableState == FREERDP_MULTITRANSPORT_TUNNEL_READY))
690 rc = rdpemt_send_subheader(multi->reliable, data, length);
691 LeaveCriticalSection(&multi->lock);
692 return rc;
693}
694
695BOOL multitransport_ended_connection(const rdpMultitransport* multi)
696{
697 WINPR_ASSERT(multi);
698 return multi->endedConnection;
699}
700
701void multitransport_reset(rdpMultitransport* multi)
702{
703 if (!multi)
704 return;
705
706 EnterCriticalSection(&multi->lock);
707 rdpEmt* emt = multi->reliable;
708 multi->reliable = nullptr;
709 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
710 multi->reliableResponseSent = FALSE;
711 LeaveCriticalSection(&multi->lock);
712 multi->endedConnection = FALSE;
713
714 rdpemt_free(emt);
715 if (multi->events)
716 {
717 /* not Queue_Clear(), it misses the events of a queue that is exactly full */
718 mt_event* ev = nullptr;
719 while ((ev = Queue_Dequeue(multi->events)))
720 mt_event_free(ev);
721 }
722 if (InterlockedCompareExchange(&multi->tunnelEventLost, 0, 1) != 0)
723 WLog_DBG(TAG, "a lost UDP tunnel event no longer matters, the tunnel is gone");
724 if (multi->event)
725 (void)ResetEvent(multi->event);
726}
727
728WINPR_ATTR_NODISCARD
729static state_run_t multitransport_server_handle_response(rdpMultitransport* multi,
730 WINPR_ATTR_UNUSED UINT32 reqId,
731 WINPR_ATTR_UNUSED UINT32 hrResponse)
732{
733 WINPR_ASSERT(multi);
734 rdpRdp* rdp = multi->rdp;
735
736 if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE))
737 return STATE_RUN_FAILED;
738
739 return STATE_RUN_CONTINUE;
740}
741
742rdpMultitransport* multitransport_new(rdpRdp* rdp, WINPR_ATTR_UNUSED UINT16 protocol)
743{
744 WINPR_ASSERT(rdp);
745
746 rdpSettings* settings = rdp->settings;
747 WINPR_ASSERT(settings);
748
749 rdpMultitransport* multi = calloc(1, sizeof(rdpMultitransport));
750 if (!multi)
751 return nullptr;
752
753 if (!InitializeCriticalSectionAndSpinCount(&multi->lock, 4000))
754 {
755 free(multi);
756 return nullptr;
757 }
758
759 multi->rdp = rdp;
760 multi->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
761 multi->events = Queue_New(TRUE, -1, -1);
762 if (!multi->event || !multi->events)
763 goto fail;
764 {
765 wObject* obj = Queue_Object(multi->events);
766 obj->fnObjectFree = mt_event_free;
767 }
768
769 if (freerdp_settings_get_bool(settings, FreeRDP_ServerMode))
770 {
771 multi->MtResponse = multitransport_server_handle_response;
772 }
773 else
774 {
775 multi->MtRequest = multitransport_client_request;
776 }
777
778 return multi;
779fail:
780 WINPR_PRAGMA_DIAG_PUSH
781 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
782 multitransport_free(multi);
783 WINPR_PRAGMA_DIAG_POP
784 return nullptr;
785}
786
787void multitransport_free(rdpMultitransport* multitransport)
788{
789 if (!multitransport)
790 return;
791
792 multitransport_reset(multitransport);
793 Queue_Free(multitransport->events);
794 if (multitransport->event)
795 (void)CloseHandle(multitransport->event);
796 DeleteCriticalSection(&multitransport->lock);
797 free(multitransport);
798}
799
800WINPR_ATTR_NODISCARD
801static rdpMultitransport* multitransport_from_context(rdpContext* context)
802{
803 if (!context || !context->rdp)
804 return nullptr;
805 return context->rdp->multitransport;
806}
807
808BOOL freerdp_multitransport_set_dvc_callbacks(rdpContext* context,
809 const FreeRDP_MultitransportDvcCallbacks* callbacks,
810 void* custom)
811{
812 rdpMultitransport* multi = multitransport_from_context(context);
813 if (!multi)
814 return FALSE;
815
816 /* multitransport_check() reads the hooks without locking, it runs on this thread (channel
817 * events come from freerdp_connect() and freerdp_disconnect(), tunnel events from
818 * freerdp_check_event_handles()). The tunnel thread may be in the old TunnelDataReceived:
819 * once it is back to raising events, it is out of it. */
820 if (multi->reliable)
821 rdpemt_clear_receiver(multi->reliable);
822 if (callbacks)
823 multi->callbacks = *callbacks;
824 else
825 memset(&multi->callbacks, 0, sizeof(multi->callbacks));
826 multi->custom = callbacks ? custom : nullptr;
827
828 multitransport_direct(multi);
829 return TRUE;
830}
831
832FreeRDP_MultitransportTunnelState freerdp_multitransport_get_tunnel_state(rdpContext* context,
833 UINT32 tunnelType)
834{
835 rdpMultitransport* multi = multitransport_from_context(context);
836 if (!multi || (tunnelType != TUNNELTYPE_UDPFECR))
837 return FREERDP_MULTITRANSPORT_TUNNEL_NONE;
838
839 EnterCriticalSection(&multi->lock);
840 const FreeRDP_MultitransportTunnelState state = multi->reliableState;
841 LeaveCriticalSection(&multi->lock);
842 return state;
843}
844
845BOOL freerdp_multitransport_send_dvc(rdpContext* context, UINT32 tunnelType, const BYTE* data,
846 size_t length)
847{
848 rdpMultitransport* multi = multitransport_from_context(context);
849 if (!multi || (tunnelType != TUNNELTYPE_UDPFECR))
850 return FALSE;
851
852 BOOL rc = FALSE;
853 EnterCriticalSection(&multi->lock);
854 if (multi->reliable && (multi->reliableState == FREERDP_MULTITRANSPORT_TUNNEL_READY))
855 rc = rdpemt_send_data(multi->reliable, data, length);
856 LeaveCriticalSection(&multi->lock);
857 return rc;
858}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Dynamic virtual channel hooks of the client side tunnels.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59