FreeRDP/libfreerdp-core/mcs.c
Pawel Jakub Dawidek 1dd33b49d5 The ber_write_length() function can store variable number of bytes (one or
three). We can't tell until we write other data and know the length, so don't
assume how much space will by consumed to store length. Bug was observed with
the mcs_write_connect_response() function, where the function assumed that
application tag will take three bytes, but it was taking five.
2012-01-17 14:17:04 +01:00

838 lines
20 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Client
* T.125 Multipoint Communication Service (MCS) Protocol
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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 "gcc.h"
#include "mcs.h"
#include "tpdu.h"
#include "tpkt.h"
/**
* 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 uint8 callingDomainSelector[1] = "\x01";
static const uint8 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"
};
*/
/**
* Read a DomainMCSPDU header.
* @param s stream
* @param domainMCSPDU DomainMCSPDU type
* @param length TPKT length
* @return
*/
boolean mcs_read_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU* domainMCSPDU, uint16* length)
{
uint8 choice;
enum DomainMCSPDU MCSPDU;
*length = tpkt_read_header(s);
if (tpdu_read_data(s) == 0)
return false;
MCSPDU = *domainMCSPDU;
per_read_choice(s, &choice);
*domainMCSPDU = (choice >> 2);
if (*domainMCSPDU != MCSPDU)
return false;
return true;
}
/**
* Write a DomainMCSPDU header.
* @param s stream
* @param domainMCSPDU DomainMCSPDU type
* @param length TPKT length
*/
void mcs_write_domain_mcspdu_header(STREAM* s, enum DomainMCSPDU domainMCSPDU, uint16 length, uint8 options)
{
tpkt_write_header(s, length);
tpdu_write_data(s);
per_write_choice(s, (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 void mcs_init_domain_parameters(DomainParameters* domainParameters,
uint32 maxChannelIds, uint32 maxUserIds, uint32 maxTokenIds, uint32 maxMCSPDUsize)
{
domainParameters->maxChannelIds = maxChannelIds;
domainParameters->maxUserIds = maxUserIds;
domainParameters->maxTokenIds = maxTokenIds;
domainParameters->maxMCSPDUsize = maxMCSPDUsize;
domainParameters->numPriorities = 1;
domainParameters->minThroughput = 0;
domainParameters->maxHeight = 1;
domainParameters->protocolVersion = 2;
}
/**
* Read MCS Domain Parameters.
* @param s stream
* @param domainParameters domain parameters
*/
boolean mcs_read_domain_parameters(STREAM* s, DomainParameters* domainParameters)
{
int length;
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));
return true;
}
/**
* Write MCS Domain Parameters.
* @param s stream
* @param domainParameters domain parameters
*/
void mcs_write_domain_parameters(STREAM* s, DomainParameters* domainParameters)
{
int length;
STREAM* tmps;
tmps = stream_new(stream_get_size(s));
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_get_length(tmps);
ber_write_sequence_tag(s, length);
stream_write(s, stream_get_head(tmps), length);
stream_free(tmps);
}
/**
* Print MCS Domain Parameters.
* @param domainParameters domain parameters
*/
void mcs_print_domain_parameters(DomainParameters* domainParameters)
{
printf("DomainParameters {\n");
printf("\tmaxChannelIds:%d\n", domainParameters->maxChannelIds);
printf("\tmaxUserIds:%d\n", domainParameters->maxUserIds);
printf("\tmaxTokenIds:%d\n", domainParameters->maxTokenIds);
printf("\tnumPriorities:%d\n", domainParameters->numPriorities);
printf("\tminThroughput:%d\n", domainParameters->minThroughput);
printf("\tmaxHeight:%d\n", domainParameters->maxHeight);
printf("\tmaxMCSPDUsize:%d\n", domainParameters->maxMCSPDUsize);
printf("\tprotocolVersion:%d\n", domainParameters->protocolVersion);
printf("}\n");
}
/**
* Read an MCS Connect Initial PDU.\n
* @msdn{cc240508}
* @param mcs MCS module
* @param s stream
*/
boolean mcs_recv_connect_initial(rdpMcs* mcs, STREAM* s)
{
int length;
boolean upwardFlag;
tpkt_read_header(s);
if (tpdu_read_data(s) == 0)
return false;
if (!ber_read_application_tag(s, MCS_TYPE_CONNECT_INITIAL, &length))
return false;
/* callingDomainSelector (OCTET_STRING) */
if (!ber_read_octet_string(s, &length))
return false;
stream_seek(s, length);
/* calledDomainSelector (OCTET_STRING) */
if (!ber_read_octet_string(s, &length))
return false;
stream_seek(s, length);
/* upwardFlag (BOOLEAN) */
if (!ber_read_boolean(s, &upwardFlag))
return false;
/* targetParameters (DomainParameters) */
mcs_read_domain_parameters(s, &mcs->targetParameters);
/* minimumParameters (DomainParameters) */
mcs_read_domain_parameters(s, &mcs->minimumParameters);
/* maximumParameters (DomainParameters) */
mcs_read_domain_parameters(s, &mcs->maximumParameters);
if (!ber_read_octet_string(s, &length))
return false;
if (!gcc_read_conference_create_request(s, mcs->transport->settings))
return false;
return true;
}
/**
* Write an MCS Connect Initial PDU.\n
* @msdn{cc240508}
* @param s stream
* @param mcs MCS module
* @param user_data GCC Conference Create Request
*/
void mcs_write_connect_initial(STREAM* s, rdpMcs* mcs, STREAM* user_data)
{
int length;
STREAM* tmps;
tmps = stream_new(stream_get_size(s));
/* 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_boolean(tmps, true);
/* targetParameters (DomainParameters) */
mcs_write_domain_parameters(tmps, &mcs->targetParameters);
/* minimumParameters (DomainParameters) */
mcs_write_domain_parameters(tmps, &mcs->minimumParameters);
/* maximumParameters (DomainParameters) */
mcs_write_domain_parameters(tmps, &mcs->maximumParameters);
/* userData (OCTET_STRING) */
ber_write_octet_string(tmps, user_data->data, stream_get_length(user_data));
length = stream_get_length(tmps);
/* Connect-Initial (APPLICATION 101, IMPLICIT SEQUENCE) */
ber_write_application_tag(s, MCS_TYPE_CONNECT_INITIAL, length);
stream_write(s, stream_get_head(tmps), length);
stream_free(tmps);
}
/**
* Write an MCS Connect Response PDU.\n
* @msdn{cc240508}
* @param s stream
* @param mcs MCS module
* @param user_data GCC Conference Create Response
*/
void mcs_write_connect_response(STREAM* s, rdpMcs* mcs, STREAM* user_data)
{
int length;
STREAM* tmps;
tmps = stream_new(stream_get_size(s));
ber_write_enumerated(tmps, 0, MCS_Result_enum_length);
ber_write_integer(tmps, 0); /* calledConnectId */
mcs->domainParameters = mcs->targetParameters;
mcs_write_domain_parameters(tmps, &(mcs->domainParameters));
/* userData (OCTET_STRING) */
ber_write_octet_string(tmps, user_data->data, stream_get_length(user_data));
length = stream_get_length(tmps);
ber_write_application_tag(s, MCS_TYPE_CONNECT_RESPONSE, length);
stream_write(s, stream_get_head(tmps), length);
stream_free(tmps);
}
/**
* Send MCS Connect Initial.\n
* @msdn{cc240508}
* @param mcs mcs module
*/
boolean mcs_send_connect_initial(rdpMcs* mcs)
{
STREAM* s;
int length;
uint8 *bm, *em;
STREAM* gcc_CCrq;
STREAM* client_data;
int status;
client_data = stream_new(512);
gcc_write_client_data_blocks(client_data, mcs->transport->settings);
gcc_CCrq = stream_new(512);
gcc_write_conference_create_request(gcc_CCrq, client_data);
length = stream_get_length(gcc_CCrq) + 7;
s = transport_send_stream_init(mcs->transport, 1024);
stream_get_mark(s, bm);
stream_seek(s, 7);
mcs_write_connect_initial(s, mcs, gcc_CCrq);
stream_get_mark(s, em);
length = (em - bm);
stream_set_mark(s, bm);
tpkt_write_header(s, length);
tpdu_write_data(s);
stream_set_mark(s, em);
status = transport_write(mcs->transport, s);
stream_free(gcc_CCrq);
stream_free(client_data);
return (status < 0 ? false : true);
}
/**
* Read MCS Connect Response.\n
* @msdn{cc240501}
* @param mcs mcs module
*/
boolean mcs_recv_connect_response(rdpMcs* mcs, STREAM* s)
{
int length;
uint8 result;
uint32 calledConnectId;
tpkt_read_header(s);
if (tpdu_read_data(s) == 0)
return false;
ber_read_application_tag(s, MCS_TYPE_CONNECT_RESPONSE, &length);
ber_read_enumerated(s, &result, MCS_Result_enum_length);
ber_read_integer(s, &calledConnectId);
if (!mcs_read_domain_parameters(s, &(mcs->domainParameters)))
return false;
ber_read_octet_string(s, &length);
if (!gcc_read_conference_create_response(s, mcs->transport->settings))
{
printf("mcs_recv_connect_response: gcc_read_conference_create_response failed\n");
return false;
}
return true;
}
/**
* Send MCS Connect Response.\n
* @msdn{cc240501}
* @param mcs mcs module
*/
boolean mcs_send_connect_response(rdpMcs* mcs)
{
STREAM* s;
int length;
uint8 *bm, *em;
STREAM* gcc_CCrsp;
STREAM* server_data;
server_data = stream_new(512);
gcc_write_server_data_blocks(server_data, mcs->transport->settings);
gcc_CCrsp = stream_new(512);
gcc_write_conference_create_response(gcc_CCrsp, server_data);
length = stream_get_length(gcc_CCrsp) + 7;
s = transport_send_stream_init(mcs->transport, 1024);
stream_get_mark(s, bm);
stream_seek(s, 7);
mcs_write_connect_response(s, mcs, gcc_CCrsp);
stream_get_mark(s, em);
length = (em - bm);
stream_set_mark(s, bm);
tpkt_write_header(s, length);
tpdu_write_data(s);
stream_set_mark(s, em);
transport_write(mcs->transport, s);
stream_free(gcc_CCrsp);
stream_free(server_data);
return true;
}
/**
* Read MCS Erect Domain Request.\n
* @msdn{cc240523}
* @param mcs
* @param s stream
*/
boolean mcs_recv_erect_domain_request(rdpMcs* mcs, STREAM* s)
{
uint16 length;
enum DomainMCSPDU MCSPDU;
MCSPDU = DomainMCSPDU_ErectDomainRequest;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return false;
return true;
}
/**
* Send MCS Erect Domain Request.\n
* @msdn{cc240523}
* @param mcs
*/
boolean mcs_send_erect_domain_request(rdpMcs* mcs)
{
STREAM* s;
uint16 length = 12;
s = transport_send_stream_init(mcs->transport, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ErectDomainRequest, length, 0);
per_write_integer(s, 0); /* subHeight (INTEGER) */
per_write_integer(s, 0); /* subInterval (INTEGER) */
if (transport_write(mcs->transport, s) < 0)
return false;
return true;
}
/**
* Read MCS Attach User Request.\n
* @msdn{cc240524}
* @param mcs mcs module
* @param s stream
*/
boolean mcs_recv_attach_user_request(rdpMcs* mcs, STREAM* s)
{
uint16 length;
enum DomainMCSPDU MCSPDU;
MCSPDU = DomainMCSPDU_AttachUserRequest;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return false;
return true;
}
/**
* Send MCS Attach User Request.\n
* @msdn{cc240524}
* @param mcs mcs module
*/
boolean mcs_send_attach_user_request(rdpMcs* mcs)
{
STREAM* s;
uint16 length = 8;
s = transport_send_stream_init(mcs->transport, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserRequest, length, 0);
if (transport_write(mcs->transport, s) < 0)
return false;
return true;
}
/**
* Read MCS Attach User Confirm.\n
* @msdn{cc240525}
* @param mcs mcs module
*/
boolean mcs_recv_attach_user_confirm(rdpMcs* mcs, STREAM* s)
{
uint16 length;
uint8 result;
enum DomainMCSPDU MCSPDU;
MCSPDU = DomainMCSPDU_AttachUserConfirm;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return false;
per_read_enumerated(s, &result, MCS_Result_enum_length); /* result */
per_read_integer16(s, &(mcs->user_id), MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
return true;
}
/**
* Send MCS Attach User Confirm.\n
* @msdn{cc240525}
* @param mcs mcs module
*/
boolean mcs_send_attach_user_confirm(rdpMcs* mcs)
{
STREAM* s;
uint16 length = 11;
s = transport_send_stream_init(mcs->transport, length);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_AttachUserConfirm, length, 2);
per_write_enumerated(s, 0, MCS_Result_enum_length); /* result */
mcs->user_id = 1002;
per_write_integer16(s, mcs->user_id, MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
transport_write(mcs->transport, s);
return true;
}
/**
* Read MCS Channel Join Request.\n
* @msdn{cc240526}
* @param mcs mcs module
* @param s stream
*/
boolean mcs_recv_channel_join_request(rdpMcs* mcs, STREAM* s, uint16* channel_id)
{
uint16 length;
enum DomainMCSPDU MCSPDU;
uint16 user_id;
MCSPDU = DomainMCSPDU_ChannelJoinRequest;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return false;
if (!per_read_integer16(s, &user_id, MCS_BASE_CHANNEL_ID))
return false;
if (user_id != mcs->user_id)
return false;
if (!per_read_integer16(s, channel_id, 0))
return false;
return true;
}
/**
* Send MCS Channel Join Request.\n
* @msdn{cc240526}
* @param mcs mcs module
* @param channel_id channel id
*/
boolean mcs_send_channel_join_request(rdpMcs* mcs, uint16 channel_id)
{
STREAM* s;
uint16 length = 12;
s = transport_send_stream_init(mcs->transport, 12);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinRequest, length, 0);
per_write_integer16(s, mcs->user_id, MCS_BASE_CHANNEL_ID);
per_write_integer16(s, channel_id, 0);
if (transport_write(mcs->transport, s) < 0)
return false;
return true;
}
/**
* Read MCS Channel Join Confirm.\n
* @msdn{cc240527}
* @param mcs mcs module
*/
boolean mcs_recv_channel_join_confirm(rdpMcs* mcs, STREAM* s, uint16* channel_id)
{
uint16 length;
uint8 result;
uint16 initiator;
uint16 requested;
enum DomainMCSPDU MCSPDU;
MCSPDU = DomainMCSPDU_ChannelJoinConfirm;
if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length))
return false;
per_read_enumerated(s, &result, MCS_Result_enum_length); /* result */
per_read_integer16(s, &initiator, MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
per_read_integer16(s, &requested, 0); /* requested (ChannelId) */
per_read_integer16(s, channel_id, 0); /* channelId */
return true;
}
/**
* Send MCS Channel Join Confirm.\n
* @msdn{cc240527}
* @param mcs mcs module
*/
boolean mcs_send_channel_join_confirm(rdpMcs* mcs, uint16 channel_id)
{
STREAM* s;
uint16 length = 15;
s = transport_send_stream_init(mcs->transport, 15);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_ChannelJoinConfirm, length, 2);
per_write_enumerated(s, 0, MCS_Result_enum_length); /* result */
per_write_integer16(s, mcs->user_id, MCS_BASE_CHANNEL_ID); /* initiator (UserId) */
per_write_integer16(s, channel_id, 0); /* requested (ChannelId) */
per_write_integer16(s, channel_id, 0); /* channelId */
transport_write(mcs->transport, s);
return true;
}
/**
* Send MCS Disconnect Provider Ultimatum PDU.\n
* @param mcs mcs module
*/
boolean mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs)
{
STREAM* s;
uint16 length = 9;
s = transport_send_stream_init(mcs->transport, 9);
mcs_write_domain_mcspdu_header(s, DomainMCSPDU_DisconnectProviderUltimatum, length, 1);
per_write_enumerated(s, 0, 0); /* reason */
transport_write(mcs->transport, s);
return true;
}
/**
* Instantiate new MCS module.
* @param transport transport
* @return new MCS module
*/
rdpMcs* mcs_new(rdpTransport* transport)
{
rdpMcs* mcs;
mcs = (rdpMcs*) xzalloc(sizeof(rdpMcs));
if (mcs != 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);
}
return mcs;
}
/**
* Free MCS module.
* @param mcs MCS module to be freed
*/
void mcs_free(rdpMcs* mcs)
{
if (mcs != NULL)
{
xfree(mcs);
}
}