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

View File

@ -24,74 +24,62 @@
#include "rdp.h"
#include "multitransport.h"
#define TAG FREERDP_TAG("core.multitransport")
struct rdp_multitransport
{
rdpRdp* rdp;
UINT32 requestId; /* requestId (4 bytes) */
UINT16 requestedProtocol; /* requestedProtocol (2 bytes) */
UINT16 reserved; /* reserved (2 bytes) */
BYTE securityCookie[16]; /* securityCookie (16 bytes) */
MultiTransportRequestCb MtRequest;
MultiTransportResponseCb MtResponse;
/* 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);
WINPR_ASSERT(srv);
WINPR_ASSERT(client);
RDPTUNNEL_ACTION_CREATEREQUEST = 0x00,
RDPTUNNEL_ACTION_CREATERESPONSE = 0x01,
RDPTUNNEL_ACTION_DATA = 0x02
};
if (srv->requestId != client->requestId)
{
WLog_WARN(TAG,
"Multitransport PDU::requestId mismatch expected 0x08%" PRIx32
", got 0x08%" PRIx32,
srv->requestId, client->requestId);
if (abortOnError)
return FALSE;
}
#define TAG FREERDP_TAG("core.multitransport")
if (srv->requestedProtocol != client->requestedProtocol)
{
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)
state_run_t multitransport_recv_request(rdpMultitransport* multi, 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))
return STATE_RUN_FAILED;
Stream_Read_UINT32(s, multitransport->requestId); /* requestId (4 bytes) */
Stream_Read_UINT16(s, multitransport->requestedProtocol); /* requestedProtocol (2 bytes) */
Stream_Read_UINT16(s, multitransport->reserved); /* reserved (2 bytes) */
Stream_Read(s, multitransport->securityCookie,
sizeof(multitransport->securityCookie)); /* securityCookie (16 bytes) */
UINT32 requestId;
UINT16 requestedProto;
const BYTE* cookie;
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);
wStream* s = rdp_message_channel_pdu_init(multitransport->rdp);
WINPR_ASSERT(multi);
wStream* s = rdp_message_channel_pdu_init(multi->rdp);
if (!s)
return FALSE;
@ -101,20 +89,41 @@ BOOL multitransport_server_send_request(rdpMultitransport* multitransport)
return FALSE;
}
Stream_Write_UINT32(s, multitransport->requestId); /* requestId (4 bytes) */
Stream_Write_UINT16(s, multitransport->requestedProtocol); /* requestedProtocol (2 bytes) */
Stream_Write_UINT16(s, multitransport->reserved); /* reserved (2 bytes) */
Stream_Write(s, multitransport->securityCookie,
sizeof(multitransport->securityCookie)); /* securityCookie (16 bytes) */
Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
Stream_Write_UINT16(s, reqProto); /* requestedProtocol (2 bytes) */
Stream_Zero(s, 2); /* reserved (2 bytes) */
Stream_Write(s, cookie, RDPUDP_COOKIE_LEN); /* 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)
return FALSE;
@ -124,47 +133,76 @@ BOOL multitransport_client_send_response(rdpMultitransport* multitransport, HRES
return FALSE;
}
Stream_Write_UINT32(s, multitransport->requestId); /* requestId (4 bytes) */
Stream_Write_UINT16(s, multitransport->requestedProtocol); /* requestedProtocol (2 bytes) */
Stream_Write_UINT16(s, multitransport->reserved); /* reserved (2 bytes) */
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);
Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
Stream_Write_UINT32(s, hr); /* HResult (4 bytes) */
return rdp_send_message_channel_pdu(multi->rdp, s, SEC_TRANSPORT_RSP);
}
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);
WINPR_ASSERT(hr);
rdpSettings* settings = multi->rdp->settings;
WINPR_ASSERT(settings);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
return FALSE;
if (!settings->ServerMode)
{
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) */
Stream_Read_UINT16(s, multi.requestedProtocol); /* requestedProtocol (2 bytes) */
Stream_Read_UINT16(s, multi.reserved); /* reserved (2 bytes) */
Stream_Read(s, multi.securityCookie,
sizeof(multi.securityCookie)); /* securityCookie (16 bytes) */
Stream_Read_UINT32(s, *hr);
if (!multitransport_compare(multitransport, &multi))
return FALSE;
return TRUE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
return STATE_RUN_FAILED;
UINT32 requestId;
HRESULT hr;
Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
Stream_Read_UINT32(s, hr); /* hrResponse (4 bytes) */
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)
{
WINPR_ASSERT(rdp);
rdpSettings* settings = rdp->settings;
WINPR_ASSERT(settings);
rdpMultitransport* multi = calloc(1, sizeof(rdpMultitransport));
if (multi)
if (!multi)
return NULL;
if (settings->ServerMode)
{
WINPR_ASSERT(rdp);
multi->rdp = rdp;
winpr_RAND(&multi->requestId, sizeof(multi->requestId));
multi->requestedProtocol = protocol;
winpr_RAND(&multi->securityCookie, sizeof(multi->securityCookie));
multi->MtResponse = multitransport_server_handle_response;
}
else
{
multi->MtRequest = multitransport_no_udp;
}
multi->rdp = rdp;
return multi;
}

View File

@ -36,16 +36,22 @@ typedef enum
INITIATE_REQUEST_PROTOCOL_UDPFECL = 0x02
} MultitransportRequestProtocol;
FREERDP_LOCAL state_run_t multitransport_client_recv_request(rdpMultitransport* multitransport,
wStream* s);
FREERDP_LOCAL BOOL multitransport_server_send_request(rdpMultitransport* multitransport);
typedef state_run_t (*MultiTransportRequestCb)(rdpMultitransport* multi, UINT32 reqId,
UINT16 reqProto, const BYTE* cookie);
typedef state_run_t (*MultiTransportResponseCb)(rdpMultitransport* multi, UINT32 reqId,
UINT32 hrResponse);
FREERDP_LOCAL BOOL multitransport_server_recv_response(rdpMultitransport* multitransport,
wStream* s, HRESULT* hr);
FREERDP_LOCAL BOOL multitransport_client_send_response(rdpMultitransport* multitransport,
#define RDPUDP_COOKIE_LEN 16
#define RDPUDP_COOKIE_HASHLEN 32
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);
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 */

View File

@ -33,6 +33,7 @@
#include "rdp.h"
#include "peer.h"
#include "multitransport.h"
#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:
if (settings->SupportMultitransport)
{
if (!multitransport_server_send_request(rdp->multitransport))
ret = STATE_RUN_FAILED;
else
/* only UDP reliable for now, nobody does lossy UDP (MS-RDPUDP only) these days */
ret = multitransport_server_request(rdp->multitransport,
INITIATE_REQUEST_PROTOCOL_UDPFECR);
switch (ret)
{
if (rdp_server_transition_to_state(
rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE))
ret = STATE_RUN_CONTINUE;
case STATE_RUN_SUCCESS:
rdp_server_transition_to_state(
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

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)
{
HRESULT hr = E_ABORT;
/* 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;
return multitransport_recv_request(rdp->multitransport, s);
}
if (securityFlags & SEC_TRANSPORT_RSP)
{
/* Initiate Multitransport Request PDU */
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;
return multitransport_recv_response(rdp->multitransport, s);
}
if (securityFlags & SEC_LICENSE_PKT)