diff options
Diffstat (limited to 'libfreerdp/core/multitransport.c')
-rw-r--r-- | libfreerdp/core/multitransport.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/libfreerdp/core/multitransport.c b/libfreerdp/core/multitransport.c new file mode 100644 index 0000000..f37870f --- /dev/null +++ b/libfreerdp/core/multitransport.c @@ -0,0 +1,229 @@ +/** + * 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); +} |