230 lines
6.4 KiB
C
230 lines
6.4 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* MULTITRANSPORT PDUs
|
|
*
|
|
* Copyright 2014 Dell Software <Mike.McDonald@software.dell.com>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <winpr/assert.h>
|
|
#include <freerdp/config.h>
|
|
#include <freerdp/log.h>
|
|
|
|
#include "settings.h"
|
|
#include "rdp.h"
|
|
#include "multitransport.h"
|
|
|
|
struct rdp_multitransport
|
|
{
|
|
rdpRdp* rdp;
|
|
|
|
MultiTransportRequestCb MtRequest;
|
|
MultiTransportResponseCb MtResponse;
|
|
|
|
/* server-side data */
|
|
UINT32 reliableReqId;
|
|
|
|
BYTE reliableCookie[RDPUDP_COOKIE_LEN];
|
|
BYTE reliableCookieHash[RDPUDP_COOKIE_HASHLEN];
|
|
};
|
|
|
|
enum
|
|
{
|
|
RDPTUNNEL_ACTION_CREATEREQUEST = 0x00,
|
|
RDPTUNNEL_ACTION_CREATERESPONSE = 0x01,
|
|
RDPTUNNEL_ACTION_DATA = 0x02
|
|
};
|
|
|
|
#define TAG FREERDP_TAG("core.multitransport")
|
|
|
|
state_run_t multitransport_recv_request(rdpMultitransport* multi, wStream* s)
|
|
{
|
|
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;
|
|
|
|
UINT32 requestId = 0;
|
|
UINT16 requestedProto = 0;
|
|
UINT16 reserved = 0;
|
|
const BYTE* cookie = NULL;
|
|
|
|
Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
|
|
Stream_Read_UINT16(s, requestedProto); /* requestedProtocol (2 bytes) */
|
|
Stream_Read_UINT16(s, reserved); /* reserved (2 bytes) */
|
|
cookie = Stream_ConstPointer(s);
|
|
Stream_Seek(s, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
|
|
if (reserved != 0)
|
|
{
|
|
/*
|
|
* If the reserved filed is not 0 the request PDU seems to contain some extra data.
|
|
* If the reserved value is 1, then two bytes of 0 (probably a version field)
|
|
* are followed by a JSON payload (not null terminated, until the end of the packet.
|
|
* There seems to be no dedicated length field)
|
|
*
|
|
* for now just ignore all that
|
|
*/
|
|
WLog_WARN(TAG,
|
|
"reserved is %" PRIu16 " instead of 0, skipping %" PRIuz "bytes of unknown data",
|
|
reserved, Stream_GetRemainingLength(s));
|
|
Stream_SafeSeek(s, Stream_GetRemainingLength(s));
|
|
}
|
|
|
|
WINPR_ASSERT(multi->MtRequest);
|
|
return multi->MtRequest(multi, requestId, requestedProto, cookie);
|
|
}
|
|
|
|
static BOOL multitransport_request_send(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
|
|
const BYTE* cookie)
|
|
{
|
|
WINPR_ASSERT(multi);
|
|
wStream* s = rdp_message_channel_pdu_init(multi->rdp);
|
|
if (!s)
|
|
return FALSE;
|
|
|
|
if (!Stream_EnsureRemainingCapacity(s, 24))
|
|
{
|
|
Stream_Release(s);
|
|
return FALSE;
|
|
}
|
|
|
|
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(multi->rdp, s, SEC_TRANSPORT_REQ);
|
|
}
|
|
|
|
state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto)
|
|
{
|
|
WINPR_ASSERT(multi);
|
|
|
|
/* 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;
|
|
|
|
if (!Stream_EnsureRemainingCapacity(s, 28))
|
|
{
|
|
Stream_Release(s);
|
|
return FALSE;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
state_run_t multitransport_recv_response(rdpMultitransport* multi, wStream* s)
|
|
{
|
|
WINPR_ASSERT(multi && multi->rdp);
|
|
WINPR_ASSERT(s);
|
|
|
|
rdpSettings* settings = multi->rdp->settings;
|
|
WINPR_ASSERT(settings);
|
|
|
|
if (!settings->ServerMode)
|
|
{
|
|
WLog_ERR(TAG, "client is not expecting a multi-transport resp packet");
|
|
return STATE_RUN_FAILED;
|
|
}
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
|
return STATE_RUN_FAILED;
|
|
|
|
UINT32 requestId = 0;
|
|
HRESULT hr = 0;
|
|
|
|
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)
|
|
return NULL;
|
|
|
|
if (settings->ServerMode)
|
|
{
|
|
multi->MtResponse = multitransport_server_handle_response;
|
|
}
|
|
else
|
|
{
|
|
multi->MtRequest = multitransport_no_udp;
|
|
}
|
|
|
|
multi->rdp = rdp;
|
|
return multi;
|
|
}
|
|
|
|
void multitransport_free(rdpMultitransport* multitransport)
|
|
{
|
|
free(multitransport);
|
|
}
|