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>
28#include <sys/socket.h>
30#include <freerdp/log.h>
31#include <freerdp/multitransport.h>
32#include <freerdp/channels/drdynvc.h>
38#include "multitransport.h"
45 MT_EVENT_AUTODETECT_REQUEST
58struct rdp_multitransport
62 MultiTransportRequestCb MtRequest;
63 MultiTransportResponseCb MtResponse;
68 BYTE reliableCookie[RDPUDP_COOKIE_LEN];
69 BYTE reliableCookieHash[RDPUDP_COOKIE_HASHLEN];
76 FreeRDP_MultitransportTunnelState reliableState;
77 BOOL reliableResponseSent;
96 RDPTUNNEL_ACTION_CREATEREQUEST = 0x00,
97 RDPTUNNEL_ACTION_CREATERESPONSE = 0x01,
98 RDPTUNNEL_ACTION_DATA = 0x02
101#define TAG FREERDP_TAG("core.multitransport")
103state_run_t multitransport_recv_request(rdpMultitransport* multi,
wStream* s)
106 rdpSettings* settings = multi->rdp->settings;
110 WLog_ERR(TAG,
"not expecting a multi-transport request in server mode");
111 return STATE_RUN_FAILED;
114 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
115 return STATE_RUN_FAILED;
117 UINT32 requestId = 0;
118 UINT16 requestedProto = 0;
120 const BYTE* cookie =
nullptr;
122 Stream_Read_UINT32(s, requestId);
123 Stream_Read_UINT16(s, requestedProto);
124 Stream_Read_UINT16(s, reserved);
125 cookie = Stream_ConstPointer(s);
126 Stream_Seek(s, RDPUDP_COOKIE_LEN);
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;
144 WINPR_ASSERT(multi->MtRequest);
145 return multi->MtRequest(multi, requestId, requestedProto, cookie);
149static BOOL multitransport_request_send(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
153 WINPR_ASSERT(cookie);
154 UINT16 sec_flags = 0;
155 wStream* s = rdp_message_channel_pdu_init(multi->rdp, &sec_flags);
159 if (!Stream_EnsureRemainingCapacity(s, 24))
165 Stream_Write_UINT32(s, reqId);
166 Stream_Write_UINT16(s, reqProto);
168 Stream_Write(s, cookie, RDPUDP_COOKIE_LEN);
170 return rdp_send_message_channel_pdu(multi->rdp, s, sec_flags | SEC_TRANSPORT_REQ);
173state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto)
178 static UINT32 reqId = 0;
180 if (reqProto == INITIATE_REQUEST_PROTOCOL_UDPFECR)
182 multi->reliableReqId = reqId++;
183 if (winpr_RAND(multi->reliableCookie,
sizeof(multi->reliableCookie)) < 0)
184 return STATE_RUN_FAILED;
186 return multitransport_request_send(multi, multi->reliableReqId, reqProto,
187 multi->reliableCookie)
192 WLog_ERR(TAG,
"only reliable transport is supported");
193 return STATE_RUN_CONTINUE;
196BOOL multitransport_client_send_response(rdpMultitransport* multi, UINT32 reqId, HRESULT hr)
200 UINT16 sec_flags = 0;
201 wStream* s = rdp_message_channel_pdu_init(multi->rdp, &sec_flags);
205 if (!Stream_EnsureRemainingCapacity(s, 28))
211 Stream_Write_UINT32(s, reqId);
216 Stream_Write_INT32(s, hr);
217 return rdp_send_message_channel_pdu(multi->rdp, s, sec_flags | SEC_TRANSPORT_RSP);
220state_run_t multitransport_recv_response(rdpMultitransport* multi,
wStream* s)
222 WINPR_ASSERT(multi && multi->rdp);
225 rdpSettings* settings = multi->rdp->settings;
226 WINPR_ASSERT(settings);
230 WLog_ERR(TAG,
"client is not expecting a multi-transport resp packet");
231 return STATE_RUN_FAILED;
234 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
235 return STATE_RUN_FAILED;
237 UINT32 requestId = 0;
240 Stream_Read_UINT32(s, requestId);
241 Stream_Read_UINT32(s, hr);
243 state_run_t res = STATE_RUN_SUCCESS;
244 IFCALLRET(multi->MtResponse, res, multi, requestId, hr);
249static BOOL multitransport_soft_sync(
const rdpMultitransport* multi)
252 WINPR_ASSERT(multi->rdp);
255 return (flags & SOFTSYNC_TCP_TO_UDP) != 0;
259static state_run_t multitransport_decline(rdpMultitransport* multi, UINT32 reqId)
262 return multitransport_client_send_response(multi, reqId, E_ABORT) ? STATE_RUN_SUCCESS
266static void mt_event_free(
void* obj)
272 Stream_Release(ev->s);
278static BOOL multitransport_direct_data(
void* custom,
wStream* s)
280 rdpMultitransport* multi = custom;
282 return multi->callbacks.TunnelDataReceived(multi->custom, TUNNELTYPE_UDPFECR, s);
286static void multitransport_tunnel_event(
void* custom, rdpEmt* emt, RDPEMT_EVENT event,
287 const BYTE* data,
size_t length)
289 rdpMultitransport* multi = custom;
293 mt_event* ev = calloc(1,
sizeof(mt_event));
299 case RDPEMT_EVENT_READY:
300 ev->type = MT_EVENT_READY;
302 case RDPEMT_EVENT_FAILED:
303 ev->type = MT_EVENT_FAILED;
305 case RDPEMT_EVENT_AUTODETECT_REQUEST:
306 case RDPEMT_EVENT_DATA:
308 ev->type = (
event == RDPEMT_EVENT_AUTODETECT_REQUEST) ? MT_EVENT_AUTODETECT_REQUEST
314 ev->s = Stream_New(
nullptr, length);
317 Stream_Write(ev->s, data, length);
318 Stream_SealLength(ev->s);
319 Stream_ResetPosition(ev->s);
324 if (!Queue_Enqueue(multi->events, ev))
327 (void)SetEvent(multi->event);
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);
341static BOOL multitransport_resolve_server(
const rdpSettings* settings,
342 struct sockaddr_storage* address,
size_t* length)
344 WINPR_ASSERT(settings);
345 WINPR_ASSERT(address);
346 WINPR_ASSERT(length);
352 char port[16] = WINPR_C_ARRAY_INIT;
353 (void)_snprintf(port,
sizeof(port),
"%" PRIu32,
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)
364 if (result->ai_addrlen <=
sizeof(*address))
366 memcpy(address, result->ai_addr, result->ai_addrlen);
367 *length = result->ai_addrlen;
370 freeaddrinfo(result);
375static state_run_t multitransport_client_request(rdpMultitransport* multi, UINT32 reqId,
376 UINT16 reqProto,
const BYTE* cookie)
379 rdpRdp* rdp = multi->rdp;
381 rdpSettings* settings = rdp->settings;
382 WINPR_ASSERT(settings);
384 WLog_DBG(TAG,
"Initiate Multitransport Request: requestId %" PRIu32
", protocol 0x%04" PRIx16,
387 if (reqProto != INITIATE_REQUEST_PROTOCOL_UDPFECR)
389 WLog_INFO(TAG,
"declining lossy UDP, only the reliable transport is implemented");
390 return multitransport_decline(multi, reqId);
393 if (!rdpemt_is_supported())
395 WLog_DBG(TAG,
"declining UDP, built without -DWITH_RDPEUDP=ON");
396 return multitransport_decline(multi, reqId);
401 TRANSPORT_TYPE_UDP_FECR))
402 return multitransport_decline(multi, reqId);
404 EnterCriticalSection(&multi->lock);
405 const BOOL busy = (multi->reliable !=
nullptr);
406 LeaveCriticalSection(&multi->lock);
409 WLog_WARN(TAG,
"a reliable UDP tunnel already exists, declining request %" PRIu32, reqId);
410 return multitransport_decline(multi, reqId);
415 const BYTE* publicKey =
nullptr;
416 DWORD publicKeyLength = 0;
417 if (!transport_get_public_key(rdp->transport, &publicKey, &publicKeyLength) || !publicKey ||
418 (publicKeyLength == 0))
420 WLog_INFO(TAG,
"declining UDP, the main connection does not use TLS");
421 return multitransport_decline(multi, reqId);
426 const TRANSPORT_LAYER layer = transport_get_layer(rdp->transport);
427 if ((layer == TRANSPORT_LAYER_TSG) || (layer == TRANSPORT_LAYER_TSG_TLS))
429 WLog_INFO(TAG,
"declining UDP, the main connection goes through a gateway");
430 return multitransport_decline(multi, reqId);
435 WLog_INFO(TAG,
"declining UDP, the main connection goes through a proxy");
436 return multitransport_decline(multi, reqId);
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))
444 WLog_INFO(TAG,
"declining UDP, the server address is unknown");
445 return multitransport_decline(multi, reqId);
448 rdpEmt* emt = rdpemt_new(WLog_Get(TAG), reqId, cookie,
450 publicKey, publicKeyLength, multitransport_tunnel_event, multi);
452 return multitransport_decline(multi, reqId);
454 EnterCriticalSection(&multi->lock);
455 multi->reliable = emt;
456 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_PENDING;
457 multi->reliableResponseSent = FALSE;
458 LeaveCriticalSection(&multi->lock);
460 if (!rdpemt_start(emt, (
const struct sockaddr*)&address, addressLength))
462 EnterCriticalSection(&multi->lock);
463 multi->reliable =
nullptr;
464 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
465 LeaveCriticalSection(&multi->lock);
467 return multitransport_decline(multi, reqId);
472 WLog_INFO(TAG,
"setting up a reliable UDP tunnel for request %" PRIu32, reqId);
473 return STATE_RUN_SUCCESS;
478static BOOL multitransport_callback_failed(rdpMultitransport* multi,
const char* what)
481 WINPR_ASSERT(multi->rdp);
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);
490static BOOL multitransport_notify_state(rdpMultitransport* multi,
491 FreeRDP_MultitransportTunnelState state)
495 if (!multi->callbacks.TunnelStateChanged)
497 if (!multi->callbacks.TunnelStateChanged(multi->custom, TUNNELTYPE_UDPFECR, state))
498 return multitransport_callback_failed(multi,
"a tunnel state change");
503static BOOL multitransport_deliver_data(rdpMultitransport* multi, mt_event* ev)
506 WINPR_ASSERT(multi->rdp);
510 rdpAutoDetect* autodetect = multi->rdp->autodetect;
511 if (autodetect && autodetect->bandwidthMeasureStarted)
512 autodetect->bandwidthMeasureByteCount += WINPR_ASSERTING_INT_CAST(UINT32, ev->length);
514 if (!multi->callbacks.TunnelDataReceived || !ev->s)
516 WLog_DBG(TAG,
"dropping %" PRIuz
" bytes of tunnel data, no receiver", ev->length);
522 if (!multi->callbacks.TunnelDataReceived(multi->custom, TUNNELTYPE_UDPFECR, s))
523 return multitransport_callback_failed(multi,
"tunnel data");
528static BOOL multitransport_deliver_autodetect(rdpMultitransport* multi,
const mt_event* ev)
531 WINPR_ASSERT(multi->rdp);
536 return !state_run_failed(
537 autodetect_recv_tunnel_request(multi->rdp->autodetect, RDP_TRANSPORT_UDP_R, ev->s));
541static BOOL multitransport_handle_event(rdpMultitransport* multi, mt_event* ev)
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);
559 if (multitransport_soft_sync(multi))
561 if (!multitransport_client_send_response(multi, reqId, S_OK))
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))
574 case MT_EVENT_FAILED:
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);
586 const UINT32 reqId = rdpemt_get_request_id(emt);
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);
603 if (!multitransport_client_send_response(multi, reqId, E_ABORT))
606 if (!multitransport_notify_state(multi, FREERDP_MULTITRANSPORT_TUNNEL_NONE))
611 case MT_EVENT_AUTODETECT_REQUEST:
612 return multitransport_deliver_autodetect(multi, ev);
616 return multitransport_deliver_data(multi, ev);
624static void multitransport_direct(rdpMultitransport* multi)
629 if (!multi->reliable || (multi->reliableState != FREERDP_MULTITRANSPORT_TUNNEL_READY) ||
630 !multi->callbacks.DataOnTunnelThread || !multi->callbacks.TunnelDataReceived)
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");
636BOOL multitransport_check(rdpMultitransport* multi)
640 (void)ResetEvent(multi->event);
641 if (InterlockedCompareExchange(&multi->tunnelEventLost, 0, 0) != 0)
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);
647#if defined(WITH_RDPEUDP)
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;
659 BOOL handled = FALSE;
660 while (Queue_Count(multi->events) > 0)
662 mt_event* ev = Queue_Dequeue(multi->events);
665 const BOOL rc = multitransport_handle_event(multi, ev);
672 multitransport_direct(multi);
676HANDLE multitransport_get_event_handle(rdpMultitransport* multi)
682BOOL multitransport_send_autodetect_response(rdpMultitransport* multi,
const BYTE* data,
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);
695BOOL multitransport_ended_connection(
const rdpMultitransport* multi)
698 return multi->endedConnection;
701void multitransport_reset(rdpMultitransport* multi)
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;
718 mt_event* ev =
nullptr;
719 while ((ev = Queue_Dequeue(multi->events)))
722 if (InterlockedCompareExchange(&multi->tunnelEventLost, 0, 1) != 0)
723 WLog_DBG(TAG,
"a lost UDP tunnel event no longer matters, the tunnel is gone");
725 (void)ResetEvent(multi->event);
729static state_run_t multitransport_server_handle_response(rdpMultitransport* multi,
730 WINPR_ATTR_UNUSED UINT32 reqId,
731 WINPR_ATTR_UNUSED UINT32 hrResponse)
734 rdpRdp* rdp = multi->rdp;
736 if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE))
737 return STATE_RUN_FAILED;
739 return STATE_RUN_CONTINUE;
742rdpMultitransport* multitransport_new(rdpRdp* rdp, WINPR_ATTR_UNUSED UINT16 protocol)
746 rdpSettings* settings = rdp->settings;
747 WINPR_ASSERT(settings);
749 rdpMultitransport* multi = calloc(1,
sizeof(rdpMultitransport));
753 if (!InitializeCriticalSectionAndSpinCount(&multi->lock, 4000))
760 multi->event = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
761 multi->events = Queue_New(TRUE, -1, -1);
762 if (!multi->event || !multi->events)
765 wObject* obj = Queue_Object(multi->events);
771 multi->MtResponse = multitransport_server_handle_response;
775 multi->MtRequest = multitransport_client_request;
780 WINPR_PRAGMA_DIAG_PUSH
781 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
782 multitransport_free(multi);
783 WINPR_PRAGMA_DIAG_POP
787void multitransport_free(rdpMultitransport* multitransport)
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);
801static rdpMultitransport* multitransport_from_context(rdpContext* context)
803 if (!context || !context->rdp)
805 return context->rdp->multitransport;
808BOOL freerdp_multitransport_set_dvc_callbacks(rdpContext* context,
812 rdpMultitransport* multi = multitransport_from_context(context);
821 rdpemt_clear_receiver(multi->reliable);
823 multi->callbacks = *callbacks;
825 memset(&multi->callbacks, 0,
sizeof(multi->callbacks));
826 multi->custom = callbacks ? custom :
nullptr;
828 multitransport_direct(multi);
832FreeRDP_MultitransportTunnelState freerdp_multitransport_get_tunnel_state(rdpContext* context,
835 rdpMultitransport* multi = multitransport_from_context(context);
836 if (!multi || (tunnelType != TUNNELTYPE_UDPFECR))
837 return FREERDP_MULTITRANSPORT_TUNNEL_NONE;
839 EnterCriticalSection(&multi->lock);
840 const FreeRDP_MultitransportTunnelState state = multi->reliableState;
841 LeaveCriticalSection(&multi->lock);
845BOOL freerdp_multitransport_send_dvc(rdpContext* context, UINT32 tunnelType,
const BYTE* data,
848 rdpMultitransport* multi = multitransport_from_context(context);
849 if (!multi || (tunnelType != TUNNELTYPE_UDPFECR))
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);
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.
OBJECT_FREE_FN fnObjectFree