multi-transport: refactor multi-transport handling

Prepare the parts of the code that handle multi-transport to really establish
UDP connections and manage alternative transports.
This commit is contained in:
David Fort 2022-11-24 13:24:26 +01:00 committed by akallabeth
parent 67e1c2dccd
commit a1febe11f0
5 changed files with 172 additions and 128 deletions

View File

@ -190,6 +190,7 @@ typedef enum
#define TRANSPORT_TYPE_UDP_FECR 0x00000001 #define TRANSPORT_TYPE_UDP_FECR 0x00000001
#define TRANSPORT_TYPE_UDP_FECL 0x00000004 #define TRANSPORT_TYPE_UDP_FECL 0x00000004
#define TRANSPORT_TYPE_UDP_PREFERRED 0x00000100 #define TRANSPORT_TYPE_UDP_PREFERRED 0x00000100
#define SOFTSYNC_TCP_TO_UDP 0x00000200
/* Static Virtual Channel Options */ /* Static Virtual Channel Options */
#define CHANNEL_OPTION_INITIALIZED 0x80000000 #define CHANNEL_OPTION_INITIALIZED 0x80000000
@ -1023,18 +1024,18 @@ struct rdp_settings
UINT64 padding0192[192 - 150]; /* 150 */ UINT64 padding0192[192 - 150]; /* 150 */
/* Client/Server Security Data */ /* Client/Server Security Data */
ALIGN64 BOOL UseRdpSecurityLayer; /* 192 */ ALIGN64 BOOL UseRdpSecurityLayer; /* 192 */
ALIGN64 UINT32 EncryptionMethods; /* 193 */ ALIGN64 UINT32 EncryptionMethods; /* 193 */
ALIGN64 UINT32 ExtEncryptionMethods; /* 194 */ ALIGN64 UINT32 ExtEncryptionMethods; /* 194 */
ALIGN64 UINT32 EncryptionLevel; /* 195 */ ALIGN64 UINT32 EncryptionLevel; /* 195 */
ALIGN64 BYTE* ServerRandom; /* 196 */ ALIGN64 BYTE* ServerRandom; /* 196 */
ALIGN64 UINT32 ServerRandomLength; /* 197 */ ALIGN64 UINT32 ServerRandomLength; /* 197 */
ALIGN64 BYTE* ServerCertificate; /* 198 */ ALIGN64 BYTE* ServerCertificate; /* 198 */
ALIGN64 UINT32 ServerCertificateLength; /* 199 */ ALIGN64 UINT32 ServerCertificateLength; /* 199 */
ALIGN64 BYTE* ClientRandom; /* 200 */ ALIGN64 BYTE* ClientRandom; /* 200 */
ALIGN64 UINT32 ClientRandomLength; /* 201 */ ALIGN64 UINT32 ClientRandomLength; /* 201 */
ALIGN64 BOOL ServerLicenseRequired; /* 202 */ ALIGN64 BOOL ServerLicenseRequired; /* 202 */
ALIGN64 char* ServerLicenseCompanyName; /* 203 */ ALIGN64 char* ServerLicenseCompanyName; /* 203 */
ALIGN64 UINT32 ServerLicenseProductVersion; /* 204 */ ALIGN64 UINT32 ServerLicenseProductVersion; /* 204 */
ALIGN64 char* ServerLicenseProductName; /* 205 */ ALIGN64 char* ServerLicenseProductName; /* 205 */
ALIGN64 char** ServerLicenseProductIssuers; /* 206 */ ALIGN64 char** ServerLicenseProductIssuers; /* 206 */
@ -1403,9 +1404,9 @@ struct rdp_settings
*/ */
/* Capabilities */ /* Capabilities */
ALIGN64 BYTE* ReceivedCapabilities; /* 2240 */ ALIGN64 BYTE* ReceivedCapabilities; /* 2240 */
ALIGN64 UINT32 ReceivedCapabilitiesSize; /* 2241 */ ALIGN64 UINT32 ReceivedCapabilitiesSize; /* 2241 */
ALIGN64 BYTE** ReceivedCapabilityData; /* 2242 */ ALIGN64 BYTE** ReceivedCapabilityData; /* 2242 */
ALIGN64 UINT32* ReceivedCapabilityDataSizes; /* 2243 */ ALIGN64 UINT32* ReceivedCapabilityDataSizes; /* 2243 */
UINT64 padding2304[2304 - 2244]; /* 2244 */ UINT64 padding2304[2304 - 2244]; /* 2244 */

View File

@ -24,74 +24,62 @@
#include "rdp.h" #include "rdp.h"
#include "multitransport.h" #include "multitransport.h"
#define TAG FREERDP_TAG("core.multitransport")
struct rdp_multitransport struct rdp_multitransport
{ {
rdpRdp* rdp; rdpRdp* rdp;
UINT32 requestId; /* requestId (4 bytes) */ MultiTransportRequestCb MtRequest;
UINT16 requestedProtocol; /* requestedProtocol (2 bytes) */ MultiTransportResponseCb MtResponse;
UINT16 reserved; /* reserved (2 bytes) */
BYTE securityCookie[16]; /* securityCookie (16 bytes) */ /* server-side data */
UINT32 reliableReqId;
BYTE reliableCookie[RDPUDP_COOKIE_LEN];
BYTE reliableCookieHash[RDPUDP_COOKIE_HASHLEN];
}; };
static BOOL multitransport_compare(const rdpMultitransport* srv, const rdpMultitransport* client) enum
{ {
BOOL abortOnError = !freerdp_settings_get_bool(srv->rdp->settings, FreeRDP_TransportDumpReplay); RDPTUNNEL_ACTION_CREATEREQUEST = 0x00,
WINPR_ASSERT(srv); RDPTUNNEL_ACTION_CREATERESPONSE = 0x01,
WINPR_ASSERT(client); RDPTUNNEL_ACTION_DATA = 0x02
};
if (srv->requestId != client->requestId) #define TAG FREERDP_TAG("core.multitransport")
{
WLog_WARN(TAG,
"Multitransport PDU::requestId mismatch expected 0x08%" PRIx32
", got 0x08%" PRIx32,
srv->requestId, client->requestId);
if (abortOnError)
return FALSE;
}
if (srv->requestedProtocol != client->requestedProtocol) state_run_t multitransport_recv_request(rdpMultitransport* multi, wStream* s)
{
WLog_WARN(TAG,
"Multitransport PDU::requestedProtocol mismatch expected 0x08%" PRIx32
", got 0x08%" PRIx32,
srv->requestedProtocol, client->requestedProtocol);
if (abortOnError)
return FALSE;
}
if (memcmp(srv->securityCookie, client->securityCookie, sizeof(srv->securityCookie)) != 0)
{
WLog_WARN(TAG, "Multitransport PDU::securityCookie mismatch");
if (abortOnError)
return FALSE;
}
return TRUE;
}
state_run_t multitransport_client_recv_request(rdpMultitransport* multitransport, wStream* s)
{ {
WINPR_ASSERT(multitransport); WINPR_ASSERT(multi);
rdpSettings* settings = multi->rdp->settings;
if (settings->ServerMode)
{
WLog_ERR(TAG, "not expecting a multi-transport request in server mode");
return STATE_RUN_FAILED;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24)) if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
return STATE_RUN_FAILED; return STATE_RUN_FAILED;
Stream_Read_UINT32(s, multitransport->requestId); /* requestId (4 bytes) */ UINT32 requestId;
Stream_Read_UINT16(s, multitransport->requestedProtocol); /* requestedProtocol (2 bytes) */ UINT16 requestedProto;
Stream_Read_UINT16(s, multitransport->reserved); /* reserved (2 bytes) */ const BYTE* cookie;
Stream_Read(s, multitransport->securityCookie,
sizeof(multitransport->securityCookie)); /* securityCookie (16 bytes) */
return STATE_RUN_SUCCESS; Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
Stream_Read_UINT16(s, requestedProto); /* requestedProtocol (2 bytes) */
Stream_Seek(s, 2); /* reserved (2 bytes) */
cookie = Stream_Pointer(s);
Stream_Seek(s, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
WINPR_ASSERT(multi->MtRequest);
return multi->MtRequest(multi, requestId, requestedProto, cookie);
} }
BOOL multitransport_server_send_request(rdpMultitransport* multitransport) static BOOL multitransport_request_send(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
const BYTE* cookie)
{ {
WINPR_ASSERT(multitransport); WINPR_ASSERT(multi);
wStream* s = rdp_message_channel_pdu_init(multitransport->rdp); wStream* s = rdp_message_channel_pdu_init(multi->rdp);
if (!s) if (!s)
return FALSE; return FALSE;
@ -101,20 +89,41 @@ BOOL multitransport_server_send_request(rdpMultitransport* multitransport)
return FALSE; return FALSE;
} }
Stream_Write_UINT32(s, multitransport->requestId); /* requestId (4 bytes) */ Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
Stream_Write_UINT16(s, multitransport->requestedProtocol); /* requestedProtocol (2 bytes) */ Stream_Write_UINT16(s, reqProto); /* requestedProtocol (2 bytes) */
Stream_Write_UINT16(s, multitransport->reserved); /* reserved (2 bytes) */ Stream_Zero(s, 2); /* reserved (2 bytes) */
Stream_Write(s, multitransport->securityCookie, Stream_Write(s, cookie, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
sizeof(multitransport->securityCookie)); /* securityCookie (16 bytes) */
return rdp_send_message_channel_pdu(multitransport->rdp, s, SEC_TRANSPORT_REQ); return rdp_send_message_channel_pdu(multi->rdp, s, SEC_TRANSPORT_REQ);
} }
BOOL multitransport_client_send_response(rdpMultitransport* multitransport, HRESULT hr) state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto)
{ {
WINPR_ASSERT(multitransport); WINPR_ASSERT(multi);
wStream* s = rdp_message_channel_pdu_init(multitransport->rdp); /* TODO: move this static variable to the listener */
static UINT32 reqId = 0;
if (reqProto == INITIATE_REQUEST_PROTOCOL_UDPFECR)
{
multi->reliableReqId = reqId++;
winpr_RAND(multi->reliableCookie, sizeof(multi->reliableCookie));
return multitransport_request_send(multi, multi->reliableReqId, reqProto,
multi->reliableCookie)
? STATE_RUN_SUCCESS
: STATE_RUN_FAILED;
}
WLog_ERR(TAG, "only reliable transport is supported");
return STATE_RUN_CONTINUE;
}
BOOL multitransport_client_send_response(rdpMultitransport* multi, UINT32 reqId, HRESULT hr)
{
WINPR_ASSERT(multi);
wStream* s = rdp_message_channel_pdu_init(multi->rdp);
if (!s) if (!s)
return FALSE; return FALSE;
@ -124,47 +133,76 @@ BOOL multitransport_client_send_response(rdpMultitransport* multitransport, HRES
return FALSE; return FALSE;
} }
Stream_Write_UINT32(s, multitransport->requestId); /* requestId (4 bytes) */ Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
Stream_Write_UINT16(s, multitransport->requestedProtocol); /* requestedProtocol (2 bytes) */ Stream_Write_UINT32(s, hr); /* HResult (4 bytes) */
Stream_Write_UINT16(s, multitransport->reserved); /* reserved (2 bytes) */ return rdp_send_message_channel_pdu(multi->rdp, s, SEC_TRANSPORT_RSP);
Stream_Write(s, multitransport->securityCookie,
sizeof(multitransport->securityCookie)); /* securityCookie (16 bytes) */
Stream_Write_UINT32(s, hr);
return rdp_send_message_channel_pdu(multitransport->rdp, s, SEC_TRANSPORT_REQ);
} }
BOOL multitransport_server_recv_response(rdpMultitransport* multitransport, wStream* s, HRESULT* hr) state_run_t multitransport_recv_response(rdpMultitransport* multi, wStream* s)
{ {
rdpMultitransport multi = { 0 }; WINPR_ASSERT(multi && multi->rdp);
WINPR_ASSERT(s);
WINPR_ASSERT(multitransport); rdpSettings* settings = multi->rdp->settings;
WINPR_ASSERT(hr); WINPR_ASSERT(settings);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 28)) if (!settings->ServerMode)
return FALSE; {
WLog_ERR(TAG, "client is not expecting a multi-transport resp packet");
return STATE_RUN_FAILED;
}
Stream_Read_UINT32(s, multi.requestId); /* requestId (4 bytes) */ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
Stream_Read_UINT16(s, multi.requestedProtocol); /* requestedProtocol (2 bytes) */ return STATE_RUN_FAILED;
Stream_Read_UINT16(s, multi.reserved); /* reserved (2 bytes) */
Stream_Read(s, multi.securityCookie, UINT32 requestId;
sizeof(multi.securityCookie)); /* securityCookie (16 bytes) */ HRESULT hr;
Stream_Read_UINT32(s, *hr);
if (!multitransport_compare(multitransport, &multi)) Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
return FALSE; Stream_Read_UINT32(s, hr); /* hrResponse (4 bytes) */
return TRUE;
return IFCALLRESULT(STATE_RUN_SUCCESS, multi->MtResponse, multi, requestId, hr);
}
static state_run_t multitransport_no_udp(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
const BYTE* cookie)
{
return multitransport_client_send_response(multi, reqId, E_ABORT) ? STATE_RUN_SUCCESS
: STATE_RUN_FAILED;
}
static state_run_t multitransport_server_handle_response(rdpMultitransport* multi, UINT32 reqId,
UINT32 hrResponse)
{
rdpRdp* rdp = multi->rdp;
if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE))
return STATE_RUN_FAILED;
return STATE_RUN_CONTINUE;
} }
rdpMultitransport* multitransport_new(rdpRdp* rdp, UINT16 protocol) rdpMultitransport* multitransport_new(rdpRdp* rdp, UINT16 protocol)
{ {
WINPR_ASSERT(rdp);
rdpSettings* settings = rdp->settings;
WINPR_ASSERT(settings);
rdpMultitransport* multi = calloc(1, sizeof(rdpMultitransport)); rdpMultitransport* multi = calloc(1, sizeof(rdpMultitransport));
if (multi) if (!multi)
return NULL;
if (settings->ServerMode)
{ {
WINPR_ASSERT(rdp); multi->MtResponse = multitransport_server_handle_response;
multi->rdp = rdp;
winpr_RAND(&multi->requestId, sizeof(multi->requestId));
multi->requestedProtocol = protocol;
winpr_RAND(&multi->securityCookie, sizeof(multi->securityCookie));
} }
else
{
multi->MtRequest = multitransport_no_udp;
}
multi->rdp = rdp;
return multi; return multi;
} }

View File

@ -36,16 +36,22 @@ typedef enum
INITIATE_REQUEST_PROTOCOL_UDPFECL = 0x02 INITIATE_REQUEST_PROTOCOL_UDPFECL = 0x02
} MultitransportRequestProtocol; } MultitransportRequestProtocol;
FREERDP_LOCAL state_run_t multitransport_client_recv_request(rdpMultitransport* multitransport, typedef state_run_t (*MultiTransportRequestCb)(rdpMultitransport* multi, UINT32 reqId,
wStream* s); UINT16 reqProto, const BYTE* cookie);
FREERDP_LOCAL BOOL multitransport_server_send_request(rdpMultitransport* multitransport); typedef state_run_t (*MultiTransportResponseCb)(rdpMultitransport* multi, UINT32 reqId,
UINT32 hrResponse);
FREERDP_LOCAL BOOL multitransport_server_recv_response(rdpMultitransport* multitransport, #define RDPUDP_COOKIE_LEN 16
wStream* s, HRESULT* hr); #define RDPUDP_COOKIE_HASHLEN 32
FREERDP_LOCAL BOOL multitransport_client_send_response(rdpMultitransport* multitransport,
FREERDP_LOCAL state_run_t multitransport_recv_request(rdpMultitransport* multi, wStream* s);
FREERDP_LOCAL state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto);
FREERDP_LOCAL state_run_t multitransport_recv_response(rdpMultitransport* multi, wStream* s);
FREERDP_LOCAL BOOL multitransport_client_send_response(rdpMultitransport* multi, UINT32 reqId,
HRESULT hr); HRESULT hr);
FREERDP_LOCAL rdpMultitransport* multitransport_new(rdpRdp* rdp, UINT16 protocol); FREERDP_LOCAL rdpMultitransport* multitransport_new(rdpRdp* rdp, UINT16 protocol);
FREERDP_LOCAL void multitransport_free(rdpMultitransport* multitransport); FREERDP_LOCAL void multitransport_free(rdpMultitransport* multi);
#endif /* FREERDP_LIB_CORE_MULTITRANSPORT_H */ #endif /* FREERDP_LIB_CORE_MULTITRANSPORT_H */

View File

@ -33,6 +33,7 @@
#include "rdp.h" #include "rdp.h"
#include "peer.h" #include "peer.h"
#include "multitransport.h"
#define TAG FREERDP_TAG("core.peer") #define TAG FREERDP_TAG("core.peer")
@ -960,13 +961,22 @@ static state_run_t peer_recv_callback_internal(rdpTransport* transport, wStream*
case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST: case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST:
if (settings->SupportMultitransport) if (settings->SupportMultitransport)
{ {
if (!multitransport_server_send_request(rdp->multitransport)) /* only UDP reliable for now, nobody does lossy UDP (MS-RDPUDP only) these days */
ret = STATE_RUN_FAILED; ret = multitransport_server_request(rdp->multitransport,
else INITIATE_REQUEST_PROTOCOL_UDPFECR);
switch (ret)
{ {
if (rdp_server_transition_to_state( case STATE_RUN_SUCCESS:
rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE)) rdp_server_transition_to_state(
ret = STATE_RUN_CONTINUE; rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE);
break;
case STATE_RUN_CONTINUE:
/* mismatch on the supported kind of UDP transports */
rdp_server_transition_to_state(
rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE);
break;
default:
break;
} }
} }
else else

View File

@ -1205,23 +1205,12 @@ state_run_t rdp_recv_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 securit
if (securityFlags & SEC_TRANSPORT_REQ) if (securityFlags & SEC_TRANSPORT_REQ)
{ {
HRESULT hr = E_ABORT; return multitransport_recv_request(rdp->multitransport, s);
/* Initiate Multitransport Request PDU */
// TODO: This message is server -> client only
state_run_t rc = multitransport_client_recv_request(rdp->multitransport, s);
if (state_run_failed(rc))
return rc;
if (!multitransport_client_send_response(rdp->multitransport, hr))
return STATE_RUN_FAILED;
return STATE_RUN_SUCCESS;
} }
if (securityFlags & SEC_TRANSPORT_RSP) if (securityFlags & SEC_TRANSPORT_RSP)
{ {
/* Initiate Multitransport Request PDU */ return multitransport_recv_response(rdp->multitransport, s);
HRESULT hr; // TODO: Do something with this result
// TODO: This message is client -> server only
return multitransport_server_recv_response(rdp->multitransport, s, &hr) ? 0 : -1;
} }
if (securityFlags & SEC_LICENSE_PKT) if (securityFlags & SEC_LICENSE_PKT)