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 WLog_DBG(TAG,
"declining UDP, switching transport disabled (/multitransport:tcp-only)");
402 return multitransport_decline(multi, reqId);
407 TRANSPORT_TYPE_UDP_FECR))
408 return multitransport_decline(multi, reqId);
410 EnterCriticalSection(&multi->lock);
411 const BOOL busy = (multi->reliable !=
nullptr);
412 LeaveCriticalSection(&multi->lock);
415 WLog_WARN(TAG,
"a reliable UDP tunnel already exists, declining request %" PRIu32, reqId);
416 return multitransport_decline(multi, reqId);
421 const BYTE* publicKey =
nullptr;
422 DWORD publicKeyLength = 0;
423 if (!transport_get_public_key(rdp->transport, &publicKey, &publicKeyLength) || !publicKey ||
424 (publicKeyLength == 0))
426 WLog_INFO(TAG,
"declining UDP, the main connection does not use TLS");
427 return multitransport_decline(multi, reqId);
432 const TRANSPORT_LAYER layer = transport_get_layer(rdp->transport);
433 if ((layer == TRANSPORT_LAYER_TSG) || (layer == TRANSPORT_LAYER_TSG_TLS))
435 WLog_INFO(TAG,
"declining UDP, the main connection goes through a gateway");
436 return multitransport_decline(multi, reqId);
441 WLog_INFO(TAG,
"declining UDP, the main connection goes through a proxy");
442 return multitransport_decline(multi, reqId);
445 struct sockaddr_storage address = WINPR_C_ARRAY_INIT;
446 size_t addressLength = 0;
447 if (!transport_get_peer_address(rdp->transport, &address, &addressLength) &&
448 !multitransport_resolve_server(settings, &address, &addressLength))
450 WLog_INFO(TAG,
"declining UDP, the server address is unknown");
451 return multitransport_decline(multi, reqId);
454 rdpEmt* emt = rdpemt_new(WLog_Get(TAG), reqId, cookie,
456 publicKey, publicKeyLength, multitransport_tunnel_event, multi);
458 return multitransport_decline(multi, reqId);
460 EnterCriticalSection(&multi->lock);
461 multi->reliable = emt;
462 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_PENDING;
463 multi->reliableResponseSent = FALSE;
464 LeaveCriticalSection(&multi->lock);
466 if (!rdpemt_start(emt, (
const struct sockaddr*)&address, addressLength))
468 EnterCriticalSection(&multi->lock);
469 multi->reliable =
nullptr;
470 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
471 LeaveCriticalSection(&multi->lock);
473 return multitransport_decline(multi, reqId);
478 WLog_INFO(TAG,
"setting up a reliable UDP tunnel for request %" PRIu32, reqId);
479 return STATE_RUN_SUCCESS;
484static BOOL multitransport_callback_failed(rdpMultitransport* multi,
const char* what)
487 WINPR_ASSERT(multi->rdp);
490 WLog_ERR(TAG,
"the dynamic channel code failed to handle %s, ending the connection", what);
491 freerdp_set_last_error_if_not(multi->rdp->context, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
496static BOOL multitransport_notify_state(rdpMultitransport* multi,
497 FreeRDP_MultitransportTunnelState state)
501 if (!multi->callbacks.TunnelStateChanged)
503 if (!multi->callbacks.TunnelStateChanged(multi->custom, TUNNELTYPE_UDPFECR, state))
504 return multitransport_callback_failed(multi,
"a tunnel state change");
509static BOOL multitransport_deliver_data(rdpMultitransport* multi, mt_event* ev)
512 WINPR_ASSERT(multi->rdp);
516 rdpAutoDetect* autodetect = multi->rdp->autodetect;
517 if (autodetect && autodetect->bandwidthMeasureStarted)
518 autodetect->bandwidthMeasureByteCount += WINPR_ASSERTING_INT_CAST(UINT32, ev->length);
520 if (!multi->callbacks.TunnelDataReceived || !ev->s)
522 WLog_DBG(TAG,
"dropping %" PRIuz
" bytes of tunnel data, no receiver", ev->length);
528 if (!multi->callbacks.TunnelDataReceived(multi->custom, TUNNELTYPE_UDPFECR, s))
529 return multitransport_callback_failed(multi,
"tunnel data");
534static BOOL multitransport_deliver_autodetect(rdpMultitransport* multi,
const mt_event* ev)
537 WINPR_ASSERT(multi->rdp);
542 return !state_run_failed(
543 autodetect_recv_tunnel_request(multi->rdp->autodetect, RDP_TRANSPORT_UDP_R, ev->s));
547static BOOL multitransport_handle_event(rdpMultitransport* multi, mt_event* ev)
556 EnterCriticalSection(&multi->lock);
557 const UINT32 reqId = multi->reliable ? rdpemt_get_request_id(multi->reliable) : 0;
558 const BOOL haveTunnel = multi->reliable !=
nullptr;
559 LeaveCriticalSection(&multi->lock);
565 if (multitransport_soft_sync(multi))
567 if (!multitransport_client_send_response(multi, reqId, S_OK))
571 EnterCriticalSection(&multi->lock);
572 multi->reliableResponseSent = TRUE;
573 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_READY;
574 LeaveCriticalSection(&multi->lock);
575 if (!multitransport_notify_state(multi, FREERDP_MULTITRANSPORT_TUNNEL_READY))
580 case MT_EVENT_FAILED:
582 EnterCriticalSection(&multi->lock);
583 rdpEmt* emt = multi->reliable;
584 const BOOL responseSent = multi->reliableResponseSent;
585 const BOOL wasReady = multi->reliableState == FREERDP_MULTITRANSPORT_TUNNEL_READY;
586 multi->reliable =
nullptr;
587 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
588 LeaveCriticalSection(&multi->lock);
592 const UINT32 reqId = rdpemt_get_request_id(emt);
601 WLog_WARN(TAG,
"the UDP tunnel broke down, ending the connection");
602 multi->endedConnection = TRUE;
603 freerdp_set_last_error_if_not(multi->rdp->context,
604 FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
609 if (!multitransport_client_send_response(multi, reqId, E_ABORT))
612 if (!multitransport_notify_state(multi, FREERDP_MULTITRANSPORT_TUNNEL_NONE))
617 case MT_EVENT_AUTODETECT_REQUEST:
618 return multitransport_deliver_autodetect(multi, ev);
622 return multitransport_deliver_data(multi, ev);
630static void multitransport_direct(rdpMultitransport* multi)
635 if (!multi->reliable || (multi->reliableState != FREERDP_MULTITRANSPORT_TUNNEL_READY) ||
636 !multi->callbacks.DataOnTunnelThread || !multi->callbacks.TunnelDataReceived)
638 if (!rdpemt_set_receiver(multi->reliable, multitransport_direct_data, multi, multi->events))
639 WLog_DBG(TAG,
"tunnel data still waits in events, the main loop comes back for it");
642BOOL multitransport_check(rdpMultitransport* multi)
646 (void)ResetEvent(multi->event);
647 if (InterlockedCompareExchange(&multi->tunnelEventLost, 0, 0) != 0)
649 WLog_ERR(TAG,
"out of memory queueing UDP tunnel events, ending the connection");
650 freerdp_set_last_error_if_not(multi->rdp->context, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
653#if defined(WITH_RDPEUDP)
658 const UINT32 direct = rdpemt_take_direct_bytes(multi->reliable);
659 rdpAutoDetect* autodetect = multi->rdp->autodetect;
660 if (autodetect && autodetect->bandwidthMeasureStarted)
661 autodetect->bandwidthMeasureByteCount += direct;
665 BOOL handled = FALSE;
666 while (Queue_Count(multi->events) > 0)
668 mt_event* ev = Queue_Dequeue(multi->events);
671 const BOOL rc = multitransport_handle_event(multi, ev);
678 multitransport_direct(multi);
682HANDLE multitransport_get_event_handle(rdpMultitransport* multi)
688BOOL multitransport_send_autodetect_response(rdpMultitransport* multi,
const BYTE* data,
694 EnterCriticalSection(&multi->lock);
695 if (multi->reliable && (multi->reliableState == FREERDP_MULTITRANSPORT_TUNNEL_READY))
696 rc = rdpemt_send_subheader(multi->reliable, data, length);
697 LeaveCriticalSection(&multi->lock);
701BOOL multitransport_ended_connection(
const rdpMultitransport* multi)
704 return multi->endedConnection;
707void multitransport_reset(rdpMultitransport* multi)
712 EnterCriticalSection(&multi->lock);
713 rdpEmt* emt = multi->reliable;
714 multi->reliable =
nullptr;
715 multi->reliableState = FREERDP_MULTITRANSPORT_TUNNEL_NONE;
716 multi->reliableResponseSent = FALSE;
717 LeaveCriticalSection(&multi->lock);
718 multi->endedConnection = FALSE;
724 mt_event* ev =
nullptr;
725 while ((ev = Queue_Dequeue(multi->events)))
728 if (InterlockedCompareExchange(&multi->tunnelEventLost, 0, 1) != 0)
729 WLog_DBG(TAG,
"a lost UDP tunnel event no longer matters, the tunnel is gone");
731 (void)ResetEvent(multi->event);
735static state_run_t multitransport_server_handle_response(rdpMultitransport* multi,
736 WINPR_ATTR_UNUSED UINT32 reqId,
737 WINPR_ATTR_UNUSED UINT32 hrResponse)
740 rdpRdp* rdp = multi->rdp;
745 if (rdp_get_state(rdp) != CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE)
747 WLog_WARN(TAG,
"ignoring multi-transport response in state %s, expected %s",
748 rdp_get_state_string(rdp),
749 rdp_state_string(CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE));
750 return STATE_RUN_FAILED;
753 if (reqId != multi->reliableReqId)
756 TAG,
"multi-transport response for request %" PRIu32
", expected %" PRIu32
", ignoring",
757 reqId, multi->reliableReqId);
760 if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE))
761 return STATE_RUN_FAILED;
763 return STATE_RUN_CONTINUE;
766rdpMultitransport* multitransport_new(rdpRdp* rdp, WINPR_ATTR_UNUSED UINT16 protocol)
770 rdpSettings* settings = rdp->settings;
771 WINPR_ASSERT(settings);
773 rdpMultitransport* multi = calloc(1,
sizeof(rdpMultitransport));
777 if (!InitializeCriticalSectionAndSpinCount(&multi->lock, 4000))
784 multi->event = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
785 multi->events = Queue_New(TRUE, -1, -1);
786 if (!multi->event || !multi->events)
789 wObject* obj = Queue_Object(multi->events);
795 multi->MtResponse = multitransport_server_handle_response;
799 multi->MtRequest = multitransport_client_request;
804 WINPR_PRAGMA_DIAG_PUSH
805 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
806 multitransport_free(multi);
807 WINPR_PRAGMA_DIAG_POP
811void multitransport_free(rdpMultitransport* multitransport)
816 multitransport_reset(multitransport);
817 Queue_Free(multitransport->events);
818 if (multitransport->event)
819 (void)CloseHandle(multitransport->event);
820 DeleteCriticalSection(&multitransport->lock);
821 free(multitransport);
825static rdpMultitransport* multitransport_from_context(rdpContext* context)
827 if (!context || !context->rdp)
829 return context->rdp->multitransport;
832BOOL freerdp_multitransport_set_dvc_callbacks(rdpContext* context,
836 rdpMultitransport* multi = multitransport_from_context(context);
845 rdpemt_clear_receiver(multi->reliable);
847 multi->callbacks = *callbacks;
849 memset(&multi->callbacks, 0,
sizeof(multi->callbacks));
850 multi->custom = callbacks ? custom :
nullptr;
852 multitransport_direct(multi);
856FreeRDP_MultitransportTunnelState freerdp_multitransport_get_tunnel_state(rdpContext* context,
859 rdpMultitransport* multi = multitransport_from_context(context);
860 if (!multi || (tunnelType != TUNNELTYPE_UDPFECR))
861 return FREERDP_MULTITRANSPORT_TUNNEL_NONE;
863 EnterCriticalSection(&multi->lock);
864 const FreeRDP_MultitransportTunnelState state = multi->reliableState;
865 LeaveCriticalSection(&multi->lock);
869BOOL freerdp_multitransport_send_dvc(rdpContext* context, UINT32 tunnelType,
const BYTE* data,
872 rdpMultitransport* multi = multitransport_from_context(context);
873 if (!multi || (tunnelType != TUNNELTYPE_UDPFECR))
877 EnterCriticalSection(&multi->lock);
878 if (multi->reliable && (multi->reliableState == FREERDP_MULTITRANSPORT_TUNNEL_READY))
879 rc = rdpemt_send_data(multi->reliable, data, length);
880 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