mirror of https://github.com/FreeRDP/FreeRDP
1489 lines
37 KiB
C
1489 lines
37 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* T.125 Multipoint Communication Service (MCS) Protocol
|
|
*
|
|
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
|
* Copyright 2015 Thincast Technologies GmbH
|
|
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
|
* Copyright 2017 Armin Novak <armin.novak@thincast.com>
|
|
* Copyright 2017 Thincast Technologies GmbH
|
|
*
|
|
* 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 <freerdp/config.h>
|
|
|
|
#include <winpr/crt.h>
|
|
#include <winpr/assert.h>
|
|
#include <freerdp/log.h>
|
|
|
|
#include "gcc.h"
|
|
|
|
#include "mcs.h"
|
|
#include "tpdu.h"
|
|
#include "tpkt.h"
|
|
#include "client.h"
|
|
#include "connection.h"
|
|
|
|
#define TAG FREERDP_TAG("core")
|
|
|
|
/**
|
|
* T.125 MCS is defined in:
|
|
*
|
|
* http://www.itu.int/rec/T-REC-T.125-199802-I/
|
|
* ITU-T T.125 Multipoint Communication Service Protocol Specification
|
|
*/
|
|
|
|
/**
|
|
* Connect-Initial ::= [APPLICATION 101] IMPLICIT SEQUENCE
|
|
* {
|
|
* callingDomainSelector OCTET_STRING,
|
|
* calledDomainSelector OCTET_STRING,
|
|
* upwardFlag BOOLEAN,
|
|
* targetParameters DomainParameters,
|
|
* minimumParameters DomainParameters,
|
|
* maximumParameters DomainParameters,
|
|
* userData OCTET_STRING
|
|
* }
|
|
*
|
|
* DomainParameters ::= SEQUENCE
|
|
* {
|
|
* maxChannelIds INTEGER (0..MAX),
|
|
* maxUserIds INTEGER (0..MAX),
|
|
* maxTokenIds INTEGER (0..MAX),
|
|
* numPriorities INTEGER (0..MAX),
|
|
* minThroughput INTEGER (0..MAX),
|
|
* maxHeight INTEGER (0..MAX),
|
|
* maxMCSPDUsize INTEGER (0..MAX),
|
|
* protocolVersion INTEGER (0..MAX)
|
|
* }
|
|
*
|
|
* Connect-Response ::= [APPLICATION 102] IMPLICIT SEQUENCE
|
|
* {
|
|
* result Result,
|
|
* calledConnectId INTEGER (0..MAX),
|
|
* domainParameters DomainParameters,
|
|
* userData OCTET_STRING
|
|
* }
|
|
*
|
|
* Result ::= ENUMERATED
|
|
* {
|
|
* rt-successful (0),
|
|
* rt-domain-merging (1),
|
|
* rt-domain-not-hierarchical (2),
|
|
* rt-no-such-channel (3),
|
|
* rt-no-such-domain (4),
|
|
* rt-no-such-user (5),
|
|
* rt-not-admitted (6),
|
|
* rt-other-user-id (7),
|
|
* rt-parameters-unacceptable (8),
|
|
* rt-token-not-available (9),
|
|
* rt-token-not-possessed (10),
|
|
* rt-too-many-channels (11),
|
|
* rt-too-many-tokens (12),
|
|
* rt-too-many-users (13),
|
|
* rt-unspecified-failure (14),
|
|
* rt-user-rejected (15)
|
|
* }
|
|
*
|
|
* ErectDomainRequest ::= [APPLICATION 1] IMPLICIT SEQUENCE
|
|
* {
|
|
* subHeight INTEGER (0..MAX),
|
|
* subInterval INTEGER (0..MAX)
|
|
* }
|
|
*
|
|
* AttachUserRequest ::= [APPPLICATION 10] IMPLICIT SEQUENCE
|
|
* {
|
|
* }
|
|
*
|
|
* AttachUserConfirm ::= [APPLICATION 11] IMPLICIT SEQUENCE
|
|
* {
|
|
* result Result,
|
|
* initiator UserId OPTIONAL
|
|
* }
|
|
*
|
|
* ChannelJoinRequest ::= [APPLICATION 14] IMPLICIT SEQUENCE
|
|
* {
|
|
* initiator UserId,
|
|
* channelId ChannelId
|
|
* }
|
|
*
|
|
* ChannelJoinConfirm ::= [APPLICATION 15] IMPLICIT SEQUENCE
|
|
* {
|
|
* result Result,
|
|
* initiator UserId,
|
|
* requested ChannelId,
|
|
* channelId ChannelId OPTIONAL
|
|
* }
|
|
*
|
|
* SendDataRequest ::= [APPLICATION 25] IMPLICIT SEQUENCE
|
|
* {
|
|
* initiator UserId,
|
|
* channelId ChannelId,
|
|
* dataPriority DataPriority,
|
|
* segmentation Segmentation,
|
|
* userData OCTET_STRING
|
|
* }
|
|
*
|
|
* DataPriority ::= CHOICE
|
|
* {
|
|
* top NULL,
|
|
* high NULL,
|
|
* medium NULL,
|
|
* low NULL,
|
|
* ...
|
|
* }
|
|
*
|
|
* Segmentation ::= BIT_STRING
|
|
* {
|
|
* begin (0),
|
|
* end (1)
|
|
* } (SIZE(2))
|
|
*
|
|
* SendDataIndication ::= SEQUENCE
|
|
* {
|
|
* initiator UserId,
|
|
* channelId ChannelId,
|
|
* reliability BOOLEAN,
|
|
* domainReferenceID INTEGER (0..65535) OPTIONAL,
|
|
* dataPriority DataPriority,
|
|
* segmentation Segmentation,
|
|
* userData OCTET_STRING,
|
|
* totalDataSize INTEGER OPTIONAL,
|
|
* nonStandard SEQUENCE OF NonStandardParameter OPTIONAL,
|
|
* ...
|
|
* }
|
|
*
|
|
*/
|
|
|
|
static const BYTE callingDomainSelector[1] = "\x01";
|
|
static const BYTE calledDomainSelector[1] = "\x01";
|
|
|
|
/*
|
|
static const char* const mcs_result_enumerated[] =
|
|
{
|
|
"rt-successful",
|
|
"rt-domain-merging",
|
|
"rt-domain-not-hierarchical",
|
|
"rt-no-such-channel",
|
|
"rt-no-such-domain",
|
|
"rt-no-such-user",
|
|
"rt-not-admitted",
|
|
"rt-other-user-id",
|
|
"rt-parameters-unacceptable",
|
|
"rt-token-not-available",
|
|
"rt-token-not-possessed",
|
|
"rt-too-many-channels",
|
|
"rt-too-many-tokens",
|
|
"rt-too-many-users",
|
|
"rt-unspecified-failure",
|
|
"rt-user-rejected"
|
|
};
|
|
*/
|
|
|
|
const char* mcs_domain_pdu_string(DomainMCSPDU pdu)
|
|
{
|
|
switch (pdu)
|
|
{
|
|
case DomainMCSPDU_PlumbDomainIndication:
|
|
return "DomainMCSPDU_PlumbDomainIndication";
|
|
case DomainMCSPDU_ErectDomainRequest:
|
|
return "DomainMCSPDU_ErectDomainRequest";
|
|
case DomainMCSPDU_MergeChannelsRequest:
|
|
return "DomainMCSPDU_MergeChannelsRequest";
|
|
case DomainMCSPDU_MergeChannelsConfirm:
|
|
return "DomainMCSPDU_MergeChannelsConfirm";
|
|
case DomainMCSPDU_PurgeChannelsIndication:
|
|
return "DomainMCSPDU_PurgeChannelsIndication";
|
|
case DomainMCSPDU_MergeTokensRequest:
|
|
return "DomainMCSPDU_MergeTokensRequest";
|
|
case DomainMCSPDU_MergeTokensConfirm:
|
|
return "DomainMCSPDU_MergeTokensConfirm";
|
|
case DomainMCSPDU_PurgeTokensIndication:
|
|
return "DomainMCSPDU_PurgeTokensIndication";
|
|
case DomainMCSPDU_DisconnectProviderUltimatum:
|
|
return "DomainMCSPDU_DisconnectProviderUltimatum";
|
|
case DomainMCSPDU_RejectMCSPDUUltimatum:
|
|
return "DomainMCSPDU_RejectMCSPDUUltimatum";
|
|
case DomainMCSPDU_AttachUserRequest:
|
|
return "DomainMCSPDU_AttachUserRequest";
|
|
case DomainMCSPDU_AttachUserConfirm:
|
|
return "DomainMCSPDU_AttachUserConfirm";
|
|
case DomainMCSPDU_DetachUserRequest:
|
|
return "DomainMCSPDU_DetachUserRequest";
|
|
case DomainMCSPDU_DetachUserIndication:
|
|
return "DomainMCSPDU_DetachUserIndication";
|
|
case DomainMCSPDU_ChannelJoinRequest:
|
|
return "DomainMCSPDU_ChannelJoinRequest";
|
|
case DomainMCSPDU_ChannelJoinConfirm:
|
|
return "DomainMCSPDU_ChannelJoinConfirm";
|
|
case DomainMCSPDU_ChannelLeaveRequest:
|
|
return "DomainMCSPDU_ChannelLeaveRequest";
|
|
case DomainMCSPDU_ChannelConveneRequest:
|
|
return "DomainMCSPDU_ChannelConveneRequest";
|
|
case DomainMCSPDU_ChannelConveneConfirm:
|
|
return "DomainMCSPDU_ChannelConveneConfirm";
|
|
case DomainMCSPDU_ChannelDisbandRequest:
|
|
return "DomainMCSPDU_ChannelDisbandRequest";
|
|
case DomainMCSPDU_ChannelDisbandIndication:
|
|
return "DomainMCSPDU_ChannelDisbandIndication";
|
|
case DomainMCSPDU_ChannelAdmitRequest:
|
|
return "DomainMCSPDU_ChannelAdmitRequest";
|
|
case DomainMCSPDU_ChannelAdmitIndication:
|
|
return "DomainMCSPDU_ChannelAdmitIndication";
|
|
case DomainMCSPDU_ChannelExpelRequest:
|
|
return "DomainMCSPDU_ChannelExpelRequest";
|
|
case DomainMCSPDU_ChannelExpelIndication:
|
|
return "DomainMCSPDU_ChannelExpelIndication";
|
|
case DomainMCSPDU_SendDataRequest:
|
|
return "DomainMCSPDU_SendDataRequest";
|
|
case DomainMCSPDU_SendDataIndication:
|
|
return "DomainMCSPDU_SendDataIndication";
|
|
case DomainMCSPDU_UniformSendDataRequest:
|
|
return "DomainMCSPDU_UniformSendDataRequest";
|
|
case DomainMCSPDU_UniformSendDataIndication:
|
|
return "DomainMCSPDU_UniformSendDataIndication";
|
|
case DomainMCSPDU_TokenGrabRequest:
|
|
return "DomainMCSPDU_TokenGrabRequest";
|
|
case DomainMCSPDU_TokenGrabConfirm:
|
|
return "DomainMCSPDU_TokenGrabConfirm";
|
|
case DomainMCSPDU_TokenInhibitRequest:
|
|
return "DomainMCSPDU_TokenInhibitRequest";
|
|
case DomainMCSPDU_TokenInhibitConfirm:
|
|
return "DomainMCSPDU_TokenInhibitConfirm";
|
|
case DomainMCSPDU_TokenGiveRequest:
|
|
return "DomainMCSPDU_TokenGiveRequest";
|
|
case DomainMCSPDU_TokenGiveIndication:
|
|
return "DomainMCSPDU_TokenGiveIndication";
|
|
case DomainMCSPDU_TokenGiveResponse:
|
|
return "DomainMCSPDU_TokenGiveResponse";
|
|
case DomainMCSPDU_TokenGiveConfirm:
|
|
return "DomainMCSPDU_TokenGiveConfirm";
|
|
case DomainMCSPDU_TokenPleaseRequest:
|
|
return "DomainMCSPDU_TokenPleaseRequest";
|
|
case DomainMCSPDU_TokenPleaseConfirm:
|
|
return "DomainMCSPDU_TokenPleaseConfirm";
|
|
case DomainMCSPDU_TokenReleaseRequest:
|
|
return "DomainMCSPDU_TokenReleaseRequest";
|
|
case DomainMCSPDU_TokenReleaseConfirm:
|
|
return "DomainMCSPDU_TokenReleaseConfirm";
|
|
case DomainMCSPDU_TokenTestRequest:
|
|
return "DomainMCSPDU_TokenTestRequest";
|
|
case DomainMCSPDU_TokenTestConfirm:
|
|
return "DomainMCSPDU_TokenTestConfirm";
|
|
case DomainMCSPDU_enum_length:
|
|
return "DomainMCSPDU_enum_length";
|
|
default:
|
|
return "DomainMCSPDU_UNKNOWN";
|
|
}
|
|
}
|
|
|
|
static BOOL mcs_merge_domain_parameters(DomainParameters* targetParameters,
|
|
DomainParameters* minimumParameters,
|
|
DomainParameters* maximumParameters,
|
|
DomainParameters* pOutParameters);
|
|
|
|
static BOOL mcs_write_connect_initial(wStream* s, rdpMcs* mcs, wStream* userData);
|
|
static BOOL mcs_write_connect_response(wStream* s, rdpMcs* mcs, wStream* userData);
|
|
static BOOL mcs_read_domain_mcspdu_header(wStream* s, DomainMCSPDU domainMCSPDU, UINT16* length,
|
|
DomainMCSPDU* actual);
|
|
|
|
static int mcs_initialize_client_channels(rdpMcs* mcs, const rdpSettings* settings)
|
|
{
|
|
if (!mcs || !settings)
|
|
return -1;
|
|
|
|
mcs->channelCount = freerdp_settings_get_uint32(settings, FreeRDP_ChannelCount);
|
|
|
|
if (mcs->channelCount > mcs->channelMaxCount)
|
|
mcs->channelCount = mcs->channelMaxCount;
|
|
|
|
ZeroMemory(mcs->channels, sizeof(rdpMcsChannel) * mcs->channelMaxCount);
|
|
|
|
for (UINT32 index = 0; index < mcs->channelCount; index++)
|
|
{
|
|
const CHANNEL_DEF* defchannel =
|
|
freerdp_settings_get_pointer_array(settings, FreeRDP_ChannelDefArray, index);
|
|
rdpMcsChannel* cur = &mcs->channels[index];
|
|
WINPR_ASSERT(defchannel);
|
|
CopyMemory(cur->Name, defchannel->name, CHANNEL_NAME_LEN);
|
|
cur->options = defchannel->options;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read a DomainMCSPDU header.
|
|
* @param s stream
|
|
* @param domainMCSPDU DomainMCSPDU type
|
|
* @param length TPKT length
|
|
*
|
|
* @return \b TRUE for success, \b FALSE otherwise
|
|
*/
|
|
|
|
BOOL mcs_read_domain_mcspdu_header(wStream* s, DomainMCSPDU domainMCSPDU, UINT16* length,
|
|
DomainMCSPDU* actual)
|
|
{
|
|
UINT16 li = 0;
|
|
BYTE choice = 0;
|
|
|
|
if (actual)
|
|
*actual = DomainMCSPDU_invalid;
|
|
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(domainMCSPDU);
|
|
WINPR_ASSERT(length);
|
|
|
|
if (!tpkt_read_header(s, length))
|
|
return FALSE;
|
|
|
|
if (!tpdu_read_data(s, &li, *length))
|
|
return FALSE;
|
|
|
|
if (!per_read_choice(s, &choice))
|
|
return FALSE;
|
|
|
|
const DomainMCSPDU MCSPDU = (choice >> 2);
|
|
if (actual)
|
|
*actual = MCSPDU;
|
|
|
|
if (domainMCSPDU != MCSPDU)
|
|
{
|
|
WLog_ERR(TAG, "Expected MCS %s, got %s", mcs_domain_pdu_string(domainMCSPDU),
|
|
mcs_domain_pdu_string(MCSPDU));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a DomainMCSPDU header.
|
|
* @param s stream
|
|
* @param domainMCSPDU DomainMCSPDU type
|
|
* @param length TPKT length
|
|
*/
|
|
|
|
BOOL mcs_write_domain_mcspdu_header(wStream* s, DomainMCSPDU domainMCSPDU, UINT16 length,
|
|
BYTE options)
|
|
{
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT((options & ~0x03) == 0);
|
|
WINPR_ASSERT((domainMCSPDU & ~0x3F) == 0);
|
|
|
|
if (!tpkt_write_header(s, length))
|
|
return FALSE;
|
|
if (!tpdu_write_data(s))
|
|
return FALSE;
|
|
return per_write_choice(s, (BYTE)((domainMCSPDU << 2) | options));
|
|
}
|
|
|
|
/**
|
|
* Initialize MCS Domain Parameters.
|
|
* @param domainParameters domain parameters
|
|
* @param maxChannelIds max channel ids
|
|
* @param maxUserIds max user ids
|
|
* @param maxTokenIds max token ids
|
|
* @param maxMCSPDUsize max MCS PDU size
|
|
*/
|
|
|
|
static BOOL mcs_init_domain_parameters(DomainParameters* domainParameters, UINT32 maxChannelIds,
|
|
UINT32 maxUserIds, UINT32 maxTokenIds, UINT32 maxMCSPDUsize)
|
|
{
|
|
if (!domainParameters)
|
|
return FALSE;
|
|
|
|
domainParameters->maxChannelIds = maxChannelIds;
|
|
domainParameters->maxUserIds = maxUserIds;
|
|
domainParameters->maxTokenIds = maxTokenIds;
|
|
domainParameters->maxMCSPDUsize = maxMCSPDUsize;
|
|
domainParameters->numPriorities = 1;
|
|
domainParameters->minThroughput = 0;
|
|
domainParameters->maxHeight = 1;
|
|
domainParameters->protocolVersion = 2;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read MCS Domain Parameters.
|
|
* @param s stream
|
|
* @param domainParameters domain parameters
|
|
*/
|
|
|
|
static BOOL mcs_read_domain_parameters(wStream* s, DomainParameters* domainParameters)
|
|
{
|
|
size_t length = 0;
|
|
|
|
if (!s || !domainParameters)
|
|
return FALSE;
|
|
|
|
return ber_read_sequence_tag(s, &length) &&
|
|
ber_read_integer(s, &(domainParameters->maxChannelIds)) &&
|
|
ber_read_integer(s, &(domainParameters->maxUserIds)) &&
|
|
ber_read_integer(s, &(domainParameters->maxTokenIds)) &&
|
|
ber_read_integer(s, &(domainParameters->numPriorities)) &&
|
|
ber_read_integer(s, &(domainParameters->minThroughput)) &&
|
|
ber_read_integer(s, &(domainParameters->maxHeight)) &&
|
|
ber_read_integer(s, &(domainParameters->maxMCSPDUsize)) &&
|
|
ber_read_integer(s, &(domainParameters->protocolVersion));
|
|
}
|
|
|
|
/**
|
|
* Write MCS Domain Parameters.
|
|
* @param s stream
|
|
* @param domainParameters domain parameters
|
|
*/
|
|
|
|
static BOOL mcs_write_domain_parameters(wStream* s, DomainParameters* domainParameters)
|
|
{
|
|
size_t length = 0;
|
|
wStream* tmps = NULL;
|
|
|
|
if (!s || !domainParameters)
|
|
return FALSE;
|
|
|
|
tmps = Stream_New(NULL, Stream_Capacity(s));
|
|
|
|
if (!tmps)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
ber_write_integer(tmps, domainParameters->maxChannelIds);
|
|
ber_write_integer(tmps, domainParameters->maxUserIds);
|
|
ber_write_integer(tmps, domainParameters->maxTokenIds);
|
|
ber_write_integer(tmps, domainParameters->numPriorities);
|
|
ber_write_integer(tmps, domainParameters->minThroughput);
|
|
ber_write_integer(tmps, domainParameters->maxHeight);
|
|
ber_write_integer(tmps, domainParameters->maxMCSPDUsize);
|
|
ber_write_integer(tmps, domainParameters->protocolVersion);
|
|
length = Stream_GetPosition(tmps);
|
|
ber_write_sequence_tag(s, length);
|
|
Stream_Write(s, Stream_Buffer(tmps), length);
|
|
Stream_Free(tmps, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef DEBUG_MCS
|
|
/**
|
|
* Print MCS Domain Parameters.
|
|
* @param domainParameters domain parameters
|
|
*/
|
|
|
|
static void mcs_print_domain_parameters(DomainParameters* domainParameters)
|
|
{
|
|
WLog_INFO(TAG, "DomainParameters {");
|
|
|
|
if (domainParameters)
|
|
{
|
|
WLog_INFO(TAG, "\tmaxChannelIds:%" PRIu32 "", domainParameters->maxChannelIds);
|
|
WLog_INFO(TAG, "\tmaxUserIds:%" PRIu32 "", domainParameters->maxUserIds);
|
|
WLog_INFO(TAG, "\tmaxTokenIds:%" PRIu32 "", domainParameters->maxTokenIds);
|
|
WLog_INFO(TAG, "\tnumPriorities:%" PRIu32 "", domainParameters->numPriorities);
|
|
WLog_INFO(TAG, "\tminThroughput:%" PRIu32 "", domainParameters->minThroughput);
|
|
WLog_INFO(TAG, "\tmaxHeight:%" PRIu32 "", domainParameters->maxHeight);
|
|
WLog_INFO(TAG, "\tmaxMCSPDUsize:%" PRIu32 "", domainParameters->maxMCSPDUsize);
|
|
WLog_INFO(TAG, "\tprotocolVersion:%" PRIu32 "", domainParameters->protocolVersion);
|
|
}
|
|
else
|
|
WLog_INFO(TAG, "\tdomainParameters=%p", domainParameters);
|
|
|
|
WLog_INFO(TAG, "}");
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Merge MCS Domain Parameters.
|
|
* @param targetParameters target parameters
|
|
* @param minimumParameters minimum parameters
|
|
* @param maximumParameters maximum parameters
|
|
* @param pOutParameters output parameters
|
|
*
|
|
* @return \b TRUE for success, \b FALSE otherwise
|
|
*/
|
|
|
|
BOOL mcs_merge_domain_parameters(DomainParameters* targetParameters,
|
|
DomainParameters* minimumParameters,
|
|
DomainParameters* maximumParameters,
|
|
DomainParameters* pOutParameters)
|
|
{
|
|
/* maxChannelIds */
|
|
if (!targetParameters || !minimumParameters || !maximumParameters || !pOutParameters)
|
|
return FALSE;
|
|
|
|
if (targetParameters->maxChannelIds >= 4)
|
|
{
|
|
pOutParameters->maxChannelIds = targetParameters->maxChannelIds;
|
|
}
|
|
else if (maximumParameters->maxChannelIds >= 4)
|
|
{
|
|
pOutParameters->maxChannelIds = 4;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid maxChannelIds [%" PRIu32 ", %" PRIu32 "]",
|
|
targetParameters->maxChannelIds, maximumParameters->maxChannelIds);
|
|
return FALSE;
|
|
}
|
|
|
|
/* maxUserIds */
|
|
|
|
if (targetParameters->maxUserIds >= 3)
|
|
{
|
|
pOutParameters->maxUserIds = targetParameters->maxUserIds;
|
|
}
|
|
else if (maximumParameters->maxUserIds >= 3)
|
|
{
|
|
pOutParameters->maxUserIds = 3;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid maxUserIds [%" PRIu32 ", %" PRIu32 "]", targetParameters->maxUserIds,
|
|
maximumParameters->maxUserIds);
|
|
return FALSE;
|
|
}
|
|
|
|
/* maxTokenIds */
|
|
pOutParameters->maxTokenIds = targetParameters->maxTokenIds;
|
|
|
|
/* numPriorities */
|
|
|
|
if (minimumParameters->numPriorities <= 1)
|
|
{
|
|
pOutParameters->numPriorities = 1;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid numPriorities [%" PRIu32 "]", maximumParameters->numPriorities);
|
|
return FALSE;
|
|
}
|
|
|
|
/* minThroughput */
|
|
pOutParameters->minThroughput = targetParameters->minThroughput;
|
|
|
|
/* maxHeight */
|
|
|
|
if ((targetParameters->maxHeight == 1) || (minimumParameters->maxHeight <= 1))
|
|
{
|
|
pOutParameters->maxHeight = 1;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid maxHeight [%" PRIu32 ", %" PRIu32 "]", targetParameters->maxHeight,
|
|
minimumParameters->maxHeight);
|
|
return FALSE;
|
|
}
|
|
|
|
/* maxMCSPDUsize */
|
|
|
|
if (targetParameters->maxMCSPDUsize >= 1024)
|
|
{
|
|
if (targetParameters->maxMCSPDUsize <= 65528)
|
|
{
|
|
pOutParameters->maxMCSPDUsize = targetParameters->maxMCSPDUsize;
|
|
}
|
|
else if ((minimumParameters->maxMCSPDUsize >= 124) &&
|
|
(minimumParameters->maxMCSPDUsize <= 65528))
|
|
{
|
|
pOutParameters->maxMCSPDUsize = 65528;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid maxMCSPDUsize [%" PRIu32 ", %" PRIu32 "]",
|
|
targetParameters->maxMCSPDUsize, minimumParameters->maxMCSPDUsize);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (maximumParameters->maxMCSPDUsize >= 124)
|
|
{
|
|
pOutParameters->maxMCSPDUsize = maximumParameters->maxMCSPDUsize;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid maxMCSPDUsize [%" PRIu32 "]", maximumParameters->maxMCSPDUsize);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* protocolVersion */
|
|
|
|
if ((targetParameters->protocolVersion == 2) ||
|
|
((minimumParameters->protocolVersion <= 2) && (maximumParameters->protocolVersion >= 2)))
|
|
{
|
|
pOutParameters->protocolVersion = 2;
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "invalid protocolVersion [%" PRIu32 ", %" PRIu32 ", %" PRIu32 "]",
|
|
targetParameters->protocolVersion, minimumParameters->protocolVersion,
|
|
maximumParameters->protocolVersion);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read an MCS Connect Initial PDU.
|
|
* msdn{cc240508}
|
|
* @param mcs MCS module
|
|
* @param s stream
|
|
*/
|
|
|
|
BOOL mcs_recv_connect_initial(rdpMcs* mcs, wStream* s)
|
|
{
|
|
UINT16 li = 0;
|
|
size_t length = 0;
|
|
BOOL upwardFlag = FALSE;
|
|
UINT16 tlength = 0;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
WINPR_ASSERT(s);
|
|
|
|
if (!tpkt_read_header(s, &tlength))
|
|
return FALSE;
|
|
|
|
if (!tpdu_read_data(s, &li, tlength))
|
|
return FALSE;
|
|
|
|
if (!ber_read_application_tag(s, MCS_TYPE_CONNECT_INITIAL, &length))
|
|
return FALSE;
|
|
|
|
/* callingDomainSelector (OCTET_STRING) */
|
|
if (!ber_read_octet_string_tag(s, &length) ||
|
|
(!Stream_CheckAndLogRequiredLength(TAG, s, length)))
|
|
return FALSE;
|
|
|
|
Stream_Seek(s, length);
|
|
|
|
/* calledDomainSelector (OCTET_STRING) */
|
|
if (!ber_read_octet_string_tag(s, &length) ||
|
|
(!Stream_CheckAndLogRequiredLength(TAG, s, length)))
|
|
return FALSE;
|
|
|
|
Stream_Seek(s, length);
|
|
|
|
/* upwardFlag (BOOLEAN) */
|
|
if (!ber_read_BOOL(s, &upwardFlag))
|
|
return FALSE;
|
|
|
|
/* targetParameters (DomainParameters) */
|
|
if (!mcs_read_domain_parameters(s, &mcs->targetParameters))
|
|
return FALSE;
|
|
|
|
/* minimumParameters (DomainParameters) */
|
|
if (!mcs_read_domain_parameters(s, &mcs->minimumParameters))
|
|
return FALSE;
|
|
|
|
/* maximumParameters (DomainParameters) */
|
|
if (!mcs_read_domain_parameters(s, &mcs->maximumParameters))
|
|
return FALSE;
|
|
|
|
if (!ber_read_octet_string_tag(s, &length) ||
|
|
(!Stream_CheckAndLogRequiredLength(TAG, s, length)))
|
|
return FALSE;
|
|
|
|
if (!gcc_read_conference_create_request(s, mcs))
|
|
return FALSE;
|
|
|
|
if (!mcs_merge_domain_parameters(&mcs->targetParameters, &mcs->minimumParameters,
|
|
&mcs->maximumParameters, &mcs->domainParameters))
|
|
return FALSE;
|
|
|
|
return tpkt_ensure_stream_consumed(s, tlength);
|
|
}
|
|
|
|
/**
|
|
* Write an MCS Connect Initial PDU.
|
|
* msdn{cc240508}
|
|
* @param s stream
|
|
* @param mcs MCS module
|
|
* @param userData GCC Conference Create Request
|
|
*/
|
|
|
|
BOOL mcs_write_connect_initial(wStream* s, rdpMcs* mcs, wStream* userData)
|
|
{
|
|
size_t length = 0;
|
|
wStream* tmps = NULL;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!s || !mcs || !userData)
|
|
return FALSE;
|
|
|
|
tmps = Stream_New(NULL, Stream_Capacity(s));
|
|
|
|
if (!tmps)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
/* callingDomainSelector (OCTET_STRING) */
|
|
ber_write_octet_string(tmps, callingDomainSelector, sizeof(callingDomainSelector));
|
|
/* calledDomainSelector (OCTET_STRING) */
|
|
ber_write_octet_string(tmps, calledDomainSelector, sizeof(calledDomainSelector));
|
|
/* upwardFlag (BOOLEAN) */
|
|
ber_write_BOOL(tmps, TRUE);
|
|
|
|
/* targetParameters (DomainParameters) */
|
|
if (!mcs_write_domain_parameters(tmps, &mcs->targetParameters))
|
|
goto out;
|
|
|
|
/* minimumParameters (DomainParameters) */
|
|
if (!mcs_write_domain_parameters(tmps, &mcs->minimumParameters))
|
|
goto out;
|
|
|
|
/* maximumParameters (DomainParameters) */
|
|
if (!mcs_write_domain_parameters(tmps, &mcs->maximumParameters))
|
|
goto out;
|
|
|
|
/* userData (OCTET_STRING) */
|
|
ber_write_octet_string(tmps, Stream_Buffer(userData), Stream_GetPosition(userData));
|
|
length = Stream_GetPosition(tmps);
|
|
/* Connect-Initial (APPLICATION 101, IMPLICIT SEQUENCE) */
|
|
ber_write_application_tag(s, MCS_TYPE_CONNECT_INITIAL, length);
|
|
Stream_Write(s, Stream_Buffer(tmps), length);
|
|
ret = TRUE;
|
|
out:
|
|
Stream_Free(tmps, TRUE);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Write an MCS Connect Response PDU.
|
|
* msdn{cc240508}
|
|
* @param s stream
|
|
* @param mcs MCS module
|
|
* @param userData GCC Conference Create Response
|
|
*
|
|
* @return \b TRUE for success, \b FALSE otherwise
|
|
*/
|
|
|
|
BOOL mcs_write_connect_response(wStream* s, rdpMcs* mcs, wStream* userData)
|
|
{
|
|
size_t length = 0;
|
|
wStream* tmps = NULL;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!s || !mcs || !userData)
|
|
return FALSE;
|
|
|
|
tmps = Stream_New(NULL, Stream_Capacity(s));
|
|
|
|
if (!tmps)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
ber_write_enumerated(tmps, 0, MCS_Result_enum_length);
|
|
ber_write_integer(tmps, 0); /* calledConnectId */
|
|
|
|
if (!mcs_write_domain_parameters(tmps, &(mcs->domainParameters)))
|
|
goto out;
|
|
|
|
/* userData (OCTET_STRING) */
|
|
ber_write_octet_string(tmps, Stream_Buffer(userData), Stream_GetPosition(userData));
|
|
length = Stream_GetPosition(tmps);
|
|
ber_write_application_tag(s, MCS_TYPE_CONNECT_RESPONSE, length);
|
|
Stream_Write(s, Stream_Buffer(tmps), length);
|
|
ret = TRUE;
|
|
out:
|
|
Stream_Free(tmps, TRUE);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Send MCS Connect Initial.
|
|
* msdn{cc240508}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
static BOOL mcs_send_connect_initial(rdpMcs* mcs)
|
|
{
|
|
int status = -1;
|
|
size_t length = 0;
|
|
wStream* s = NULL;
|
|
size_t bm = 0;
|
|
size_t em = 0;
|
|
wStream* gcc_CCrq = NULL;
|
|
wStream* client_data = NULL;
|
|
rdpContext* context = NULL;
|
|
|
|
if (!mcs)
|
|
return FALSE;
|
|
|
|
context = transport_get_context(mcs->transport);
|
|
WINPR_ASSERT(context);
|
|
|
|
mcs_initialize_client_channels(mcs, context->settings);
|
|
client_data = Stream_New(NULL, 512);
|
|
|
|
if (!client_data)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gcc_write_client_data_blocks(client_data, mcs))
|
|
goto out;
|
|
gcc_CCrq = Stream_New(NULL, 1024);
|
|
|
|
if (!gcc_CCrq)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
goto out;
|
|
}
|
|
|
|
if (!gcc_write_conference_create_request(gcc_CCrq, client_data))
|
|
goto out;
|
|
length = Stream_GetPosition(gcc_CCrq) + 7;
|
|
s = Stream_New(NULL, 1024 + length);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
goto out;
|
|
}
|
|
|
|
bm = Stream_GetPosition(s);
|
|
Stream_Seek(s, 7);
|
|
|
|
if (!mcs_write_connect_initial(s, mcs, gcc_CCrq))
|
|
{
|
|
WLog_ERR(TAG, "mcs_write_connect_initial failed!");
|
|
goto out;
|
|
}
|
|
|
|
em = Stream_GetPosition(s);
|
|
length = (em - bm);
|
|
if (length > UINT16_MAX)
|
|
goto out;
|
|
Stream_SetPosition(s, bm);
|
|
if (!tpkt_write_header(s, (UINT16)length))
|
|
goto out;
|
|
if (!tpdu_write_data(s))
|
|
goto out;
|
|
Stream_SetPosition(s, em);
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
out:
|
|
Stream_Free(s, TRUE);
|
|
Stream_Free(gcc_CCrq, TRUE);
|
|
Stream_Free(client_data, TRUE);
|
|
return (status < 0 ? FALSE : TRUE);
|
|
}
|
|
|
|
/**
|
|
* Read MCS Connect Response.
|
|
* msdn{cc240501}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_recv_connect_response(rdpMcs* mcs, wStream* s)
|
|
{
|
|
size_t length = 0;
|
|
UINT16 tlength = 0;
|
|
BYTE result = 0;
|
|
UINT16 li = 0;
|
|
UINT32 calledConnectId = 0;
|
|
|
|
if (!mcs || !s)
|
|
return FALSE;
|
|
|
|
if (!tpkt_read_header(s, &tlength))
|
|
return FALSE;
|
|
|
|
if (!tpdu_read_data(s, &li, tlength))
|
|
return FALSE;
|
|
|
|
if (!ber_read_application_tag(s, MCS_TYPE_CONNECT_RESPONSE, &length) ||
|
|
!ber_read_enumerated(s, &result, MCS_Result_enum_length) ||
|
|
!ber_read_integer(s, &calledConnectId) ||
|
|
!mcs_read_domain_parameters(s, &(mcs->domainParameters)) ||
|
|
!ber_read_octet_string_tag(s, &length))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gcc_read_conference_create_response(s, mcs))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_conference_create_response failed");
|
|
return FALSE;
|
|
}
|
|
|
|
return tpkt_ensure_stream_consumed(s, tlength);
|
|
}
|
|
|
|
/**
|
|
* Send MCS Connect Response.
|
|
* msdn{cc240501}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_send_connect_response(rdpMcs* mcs)
|
|
{
|
|
size_t length = 0;
|
|
int status = -1;
|
|
wStream* s = NULL;
|
|
size_t bm = 0;
|
|
size_t em = 0;
|
|
wStream* gcc_CCrsp = NULL;
|
|
wStream* server_data = NULL;
|
|
|
|
if (!mcs)
|
|
return FALSE;
|
|
|
|
server_data = Stream_New(NULL, 512);
|
|
|
|
if (!server_data)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gcc_write_server_data_blocks(server_data, mcs))
|
|
goto out;
|
|
|
|
gcc_CCrsp = Stream_New(NULL, 512 + Stream_Capacity(server_data));
|
|
|
|
if (!gcc_CCrsp)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
goto out;
|
|
}
|
|
|
|
if (!gcc_write_conference_create_response(gcc_CCrsp, server_data))
|
|
goto out;
|
|
length = Stream_GetPosition(gcc_CCrsp) + 7;
|
|
s = Stream_New(NULL, length + 1024);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
goto out;
|
|
}
|
|
|
|
bm = Stream_GetPosition(s);
|
|
Stream_Seek(s, 7);
|
|
|
|
if (!mcs_write_connect_response(s, mcs, gcc_CCrsp))
|
|
goto out;
|
|
|
|
em = Stream_GetPosition(s);
|
|
length = (em - bm);
|
|
if (length > UINT16_MAX)
|
|
goto out;
|
|
Stream_SetPosition(s, bm);
|
|
if (!tpkt_write_header(s, (UINT16)length))
|
|
goto out;
|
|
if (!tpdu_write_data(s))
|
|
goto out;
|
|
Stream_SetPosition(s, em);
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
out:
|
|
Stream_Free(s, TRUE);
|
|
Stream_Free(gcc_CCrsp, TRUE);
|
|
Stream_Free(server_data, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read MCS Erect Domain Request.
|
|
* msdn{cc240523}
|
|
* @param mcs MCS module to use
|
|
* @param s stream
|
|
*/
|
|
|
|
BOOL mcs_recv_erect_domain_request(rdpMcs* mcs, wStream* s)
|
|
{
|
|
UINT16 length = 0;
|
|
UINT32 subHeight = 0;
|
|
UINT32 subInterval = 0;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
WINPR_ASSERT(s);
|
|
|
|
if (!mcs_read_domain_mcspdu_header(s, DomainMCSPDU_ErectDomainRequest, &length, NULL))
|
|
return FALSE;
|
|
|
|
if (!per_read_integer(s, &subHeight)) /* subHeight (INTEGER) */
|
|
return FALSE;
|
|
|
|
if (!per_read_integer(s, &subInterval)) /* subInterval (INTEGER) */
|
|
return FALSE;
|
|
|
|
return tpkt_ensure_stream_consumed(s, length);
|
|
}
|
|
|
|
/**
|
|
* Send MCS Erect Domain Request.
|
|
* msdn{cc240523}
|
|
* @param mcs MCS module to use
|
|
*/
|
|
|
|
BOOL mcs_send_erect_domain_request(rdpMcs* mcs)
|
|
{
|
|
wStream* s = NULL;
|
|
int status = 0;
|
|
UINT16 length = 12;
|
|
|
|
if (!mcs)
|
|
return FALSE;
|
|
|
|
s = Stream_New(NULL, length);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ErectDomainRequest, length, 0);
|
|
per_write_integer(s, 0); /* subHeight (INTEGER) */
|
|
per_write_integer(s, 0); /* subInterval (INTEGER) */
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
Stream_Free(s, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read MCS Attach User Request.
|
|
* msdn{cc240524}
|
|
* @param mcs mcs module
|
|
* @param s stream
|
|
*/
|
|
|
|
BOOL mcs_recv_attach_user_request(rdpMcs* mcs, wStream* s)
|
|
{
|
|
UINT16 length = 0;
|
|
|
|
if (!mcs || !s)
|
|
return FALSE;
|
|
|
|
if (!mcs_read_domain_mcspdu_header(s, DomainMCSPDU_AttachUserRequest, &length, NULL))
|
|
return FALSE;
|
|
return tpkt_ensure_stream_consumed(s, length);
|
|
}
|
|
|
|
/**
|
|
* Send MCS Attach User Request.
|
|
* msdn{cc240524}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_send_attach_user_request(rdpMcs* mcs)
|
|
{
|
|
wStream* s = NULL;
|
|
int status = 0;
|
|
UINT16 length = 8;
|
|
|
|
if (!mcs)
|
|
return FALSE;
|
|
|
|
s = Stream_New(NULL, length);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserRequest, length, 0);
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
Stream_Free(s, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read MCS Attach User Confirm.
|
|
* msdn{cc240525}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_recv_attach_user_confirm(rdpMcs* mcs, wStream* s)
|
|
{
|
|
BYTE result = 0;
|
|
UINT16 length = 0;
|
|
|
|
if (!mcs || !s)
|
|
return FALSE;
|
|
|
|
if (!mcs_read_domain_mcspdu_header(s, DomainMCSPDU_AttachUserConfirm, &length, NULL))
|
|
return FALSE;
|
|
if (!per_read_enumerated(s, &result, MCS_Result_enum_length)) /* result */
|
|
return FALSE;
|
|
if (!per_read_integer16(s, &(mcs->userId), MCS_BASE_CHANNEL_ID)) /* initiator (UserId) */
|
|
return FALSE;
|
|
return tpkt_ensure_stream_consumed(s, length);
|
|
}
|
|
|
|
/**
|
|
* Send MCS Attach User Confirm.
|
|
* msdn{cc240525}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_send_attach_user_confirm(rdpMcs* mcs)
|
|
{
|
|
wStream* s = NULL;
|
|
int status = 0;
|
|
UINT16 length = 11;
|
|
|
|
if (!mcs)
|
|
return FALSE;
|
|
|
|
s = Stream_New(NULL, length);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
mcs->userId = mcs->baseChannelId++;
|
|
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserConfirm, length, 2);
|
|
per_write_enumerated(s, 0, MCS_Result_enum_length); /* result */
|
|
per_write_integer16(s, mcs->userId, MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
Stream_Free(s, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read MCS Channel Join Request.
|
|
* msdn{cc240526}
|
|
* @param mcs mcs module
|
|
* @param s stream
|
|
*/
|
|
|
|
BOOL mcs_recv_channel_join_request(rdpMcs* mcs, const rdpSettings* settings, wStream* s,
|
|
UINT16* channelId)
|
|
{
|
|
UINT16 length = 0;
|
|
UINT16 userId = 0;
|
|
|
|
if (!mcs || !s || !channelId)
|
|
return FALSE;
|
|
|
|
if (!mcs_read_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinRequest, &length, NULL))
|
|
return FALSE;
|
|
|
|
if (!per_read_integer16(s, &userId, MCS_BASE_CHANNEL_ID))
|
|
return FALSE;
|
|
if (userId != mcs->userId)
|
|
{
|
|
if (freerdp_settings_get_bool(settings, FreeRDP_TransportDumpReplay))
|
|
mcs->userId = userId;
|
|
else
|
|
return FALSE;
|
|
}
|
|
if (!per_read_integer16(s, channelId, 0))
|
|
return FALSE;
|
|
|
|
return tpkt_ensure_stream_consumed(s, length);
|
|
}
|
|
|
|
/**
|
|
* Send MCS Channel Join Request.
|
|
* msdn{cc240526}
|
|
*
|
|
* @param mcs mcs module
|
|
* @param channelId channel id
|
|
*
|
|
* @return \b TRUE for success, \b FALSE otherwise
|
|
*/
|
|
|
|
BOOL mcs_send_channel_join_request(rdpMcs* mcs, UINT16 channelId)
|
|
{
|
|
wStream* s = NULL;
|
|
int status = 0;
|
|
UINT16 length = 12;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
|
|
s = Stream_New(NULL, length);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinRequest, length, 0);
|
|
per_write_integer16(s, mcs->userId, MCS_BASE_CHANNEL_ID);
|
|
per_write_integer16(s, channelId, 0);
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
Stream_Free(s, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Read MCS Channel Join Confirm.
|
|
* msdn{cc240527}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_recv_channel_join_confirm(rdpMcs* mcs, wStream* s, UINT16* channelId)
|
|
{
|
|
UINT16 length = 0;
|
|
BYTE result = 0;
|
|
UINT16 initiator = 0;
|
|
UINT16 requested = 0;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
WINPR_ASSERT(channelId);
|
|
|
|
if (!mcs_read_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinConfirm, &length, NULL))
|
|
return FALSE;
|
|
|
|
if (!per_read_enumerated(s, &result, MCS_Result_enum_length)) /* result */
|
|
return FALSE;
|
|
if (!per_read_integer16(s, &initiator, MCS_BASE_CHANNEL_ID)) /* initiator (UserId) */
|
|
return FALSE;
|
|
if (!per_read_integer16(s, &requested, 0)) /* requested (ChannelId) */
|
|
return FALSE;
|
|
if (!per_read_integer16(s, channelId, 0)) /* channelId */
|
|
return FALSE;
|
|
return tpkt_ensure_stream_consumed(s, length);
|
|
}
|
|
|
|
/**
|
|
* Send MCS Channel Join Confirm.
|
|
* msdn{cc240527}
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channelId)
|
|
{
|
|
wStream* s = NULL;
|
|
int status = -1;
|
|
UINT16 length = 15;
|
|
|
|
if (!mcs)
|
|
return FALSE;
|
|
|
|
s = Stream_New(NULL, length);
|
|
|
|
if (!s)
|
|
{
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinConfirm, length, 2))
|
|
goto fail;
|
|
if (!per_write_enumerated(s, 0, MCS_Result_enum_length)) /* result */
|
|
goto fail;
|
|
if (!per_write_integer16(s, mcs->userId, MCS_BASE_CHANNEL_ID)) /* initiator (UserId) */
|
|
goto fail;
|
|
if (!per_write_integer16(s, channelId, 0)) /* requested (ChannelId) */
|
|
goto fail;
|
|
if (!per_write_integer16(s, channelId, 0)) /* channelId */
|
|
goto fail;
|
|
Stream_SealLength(s);
|
|
status = transport_write(mcs->transport, s);
|
|
fail:
|
|
Stream_Free(s, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Receive MCS Disconnect Provider Ultimatum PDU.
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason)
|
|
{
|
|
BYTE b1 = 0;
|
|
BYTE b2 = 0;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(reason);
|
|
|
|
/*
|
|
* http://msdn.microsoft.com/en-us/library/cc240872.aspx:
|
|
*
|
|
* PER encoded (ALIGNED variant of BASIC-PER) PDU contents:
|
|
* 21 80
|
|
*
|
|
* 0x21:
|
|
* 0 - --\
|
|
* 0 - |
|
|
* 1 - | CHOICE: From DomainMCSPDU select disconnectProviderUltimatum (8)
|
|
* 0 - | of type DisconnectProviderUltimatum
|
|
* 0 - |
|
|
* 0 - --/
|
|
* 0 - --\
|
|
* 1 - |
|
|
* | DisconnectProviderUltimatum::reason = rn-user-requested (3)
|
|
* 0x80: |
|
|
* 1 - --/
|
|
* 0 - padding
|
|
* 0 - padding
|
|
* 0 - padding
|
|
* 0 - padding
|
|
* 0 - padding
|
|
* 0 - padding
|
|
* 0 - padding
|
|
*/
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
|
return FALSE;
|
|
|
|
Stream_Rewind_UINT8(s);
|
|
Stream_Read_UINT8(s, b1);
|
|
Stream_Read_UINT8(s, b2);
|
|
*reason = ((b1 & 0x01) << 1) | (b2 >> 7);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Send MCS Disconnect Provider Ultimatum PDU.
|
|
* @param mcs mcs module
|
|
*/
|
|
|
|
BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs)
|
|
{
|
|
wStream* s = NULL;
|
|
int status = -1;
|
|
UINT16 length = 9;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
|
|
s = Stream_New(NULL, length);
|
|
|
|
if (!s)
|
|
goto fail;
|
|
|
|
if (!mcs_write_domain_mcspdu_header(s, DomainMCSPDU_DisconnectProviderUltimatum, length, 1))
|
|
goto fail;
|
|
|
|
if (!per_write_enumerated(s, 0x80, 0))
|
|
goto fail;
|
|
status = transport_write(mcs->transport, s);
|
|
fail:
|
|
Stream_Free(s, TRUE);
|
|
return (status < 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
BOOL mcs_client_begin(rdpMcs* mcs)
|
|
{
|
|
rdpContext* context = NULL;
|
|
|
|
if (!mcs || !mcs->transport)
|
|
return FALSE;
|
|
|
|
context = transport_get_context(mcs->transport);
|
|
|
|
if (!context)
|
|
return FALSE;
|
|
|
|
/* First transition state, we need this to trigger session recording */
|
|
if (!mcs_send_connect_initial(mcs))
|
|
{
|
|
freerdp_set_last_error_if_not(context, FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR);
|
|
|
|
WLog_ERR(TAG, "Error: unable to send MCS Connect Initial");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Instantiate new MCS module.
|
|
* @param transport transport
|
|
* @return new MCS module
|
|
*/
|
|
|
|
rdpMcs* mcs_new(rdpTransport* transport)
|
|
{
|
|
rdpMcs* mcs = NULL;
|
|
|
|
mcs = (rdpMcs*)calloc(1, sizeof(rdpMcs));
|
|
|
|
if (!mcs)
|
|
return NULL;
|
|
|
|
mcs->transport = transport;
|
|
mcs_init_domain_parameters(&mcs->targetParameters, 34, 2, 0, 0xFFFF);
|
|
mcs_init_domain_parameters(&mcs->minimumParameters, 1, 1, 1, 0x420);
|
|
mcs_init_domain_parameters(&mcs->maximumParameters, 0xFFFF, 0xFC17, 0xFFFF, 0xFFFF);
|
|
mcs_init_domain_parameters(&mcs->domainParameters, 0, 0, 0, 0xFFFF);
|
|
mcs->channelCount = 0;
|
|
mcs->channelMaxCount = CHANNEL_MAX_COUNT;
|
|
mcs->baseChannelId = MCS_GLOBAL_CHANNEL_ID + 1;
|
|
mcs->channels = (rdpMcsChannel*)calloc(mcs->channelMaxCount, sizeof(rdpMcsChannel));
|
|
|
|
if (!mcs->channels)
|
|
goto out_free;
|
|
|
|
return mcs;
|
|
out_free:
|
|
free(mcs);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Free MCS module.
|
|
* @param mcs MCS module to be freed
|
|
*/
|
|
|
|
void mcs_free(rdpMcs* mcs)
|
|
{
|
|
if (mcs)
|
|
{
|
|
free(mcs->channels);
|
|
free(mcs);
|
|
}
|
|
}
|
|
|
|
BOOL mcs_server_apply_to_settings(const rdpMcs* mcs, rdpSettings* settings)
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
WINPR_ASSERT(mcs);
|
|
WINPR_ASSERT(settings);
|
|
|
|
if (!freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, mcs->channelCount))
|
|
goto fail;
|
|
|
|
for (UINT32 x = 0; x < mcs->channelCount; x++)
|
|
{
|
|
const rdpMcsChannel* current = &mcs->channels[x];
|
|
CHANNEL_DEF def = { 0 };
|
|
def.options = current->options;
|
|
memcpy(def.name, current->Name, sizeof(def.name));
|
|
if (!freerdp_settings_set_pointer_array(settings, FreeRDP_ChannelDefArray, x, &def))
|
|
goto fail;
|
|
}
|
|
|
|
rc = TRUE;
|
|
fail:
|
|
if (!rc)
|
|
WLog_WARN(TAG, "failed to apply settings");
|
|
|
|
return rc;
|
|
}
|