use ironrdp_core::{
cast_length, ensure_size, invalid_field_err, other_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor,
WriteCursor,
};
use super::{ClientGccBlocks, ServerGccBlocks};
use crate::{mcs, per};
const CONFERENCE_REQUEST_OBJECT_ID: [u8; 6] = [0, 0, 20, 124, 0, 1];
const CONFERENCE_REQUEST_CLIENT_TO_SERVER_H221_NON_STANDARD: &[u8; 4] = b"Duca";
const CONFERENCE_REQUEST_SERVER_TO_CLIENT_H221_NON_STANDARD: &[u8; 4] = b"McDn";
const CONFERENCE_REQUEST_U16_MIN: u16 = 1001;
const CONFERENCE_REQUEST_CONNECT_PDU_SIZE: usize = 12;
const CONFERENCE_RESPONSE_CONNECT_PDU_SIZE: usize = 13;
const OBJECT_IDENTIFIER_KEY: u8 = 0;
const CONNECT_GCC_PDU_CONFERENCE_REQUEST_CHOICE: u8 = 0;
const CONNECT_GCC_PDU_CONFERENCE_RESPONSE_CHOICE: u8 = 0x14;
const CONFERENCE_REQUEST_USER_DATA_SELECTION: u8 = 8;
const USER_DATA_NUMBER_OF_SETS: u8 = 1;
const USER_DATA_H221_NON_STANDARD_CHOICE: u8 = 0xc0;
const CONFERENCE_RESPONSE_TAG: u32 = 1;
const CONFERENCE_RESPONSE_RESULT: u8 = 0;
const H221_NON_STANDARD_MIN_LENGTH: usize = 4;
const CONFERENCE_NAME: &[u8] = b"1";
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConferenceCreateRequest {
pub gcc_blocks: ClientGccBlocks,
}
impl ConferenceCreateRequest {
const NAME: &'static str = "ConferenceCreateRequest";
}
impl Encode for ConferenceCreateRequest {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in:dst, size: self.size());
let gcc_blocks_buffer_length = self.gcc_blocks.size();
per::write_choice(dst, OBJECT_IDENTIFIER_KEY);
per::write_object_id(dst, CONFERENCE_REQUEST_OBJECT_ID);
per::write_length(
dst,
cast_length!(
"gccBlocksLen",
gcc_blocks_buffer_length + CONFERENCE_REQUEST_CONNECT_PDU_SIZE
)?,
);
per::write_choice(dst, CONNECT_GCC_PDU_CONFERENCE_REQUEST_CHOICE);
per::write_selection(dst, CONFERENCE_REQUEST_USER_DATA_SELECTION);
per::write_numeric_string(dst, CONFERENCE_NAME, 1).map_err(|e| other_err!("confName", source: e))?;
per::write_padding(dst, 1);
per::write_number_of_sets(dst, USER_DATA_NUMBER_OF_SETS);
per::write_choice(dst, USER_DATA_H221_NON_STANDARD_CHOICE);
per::write_octet_string(
dst,
CONFERENCE_REQUEST_CLIENT_TO_SERVER_H221_NON_STANDARD,
H221_NON_STANDARD_MIN_LENGTH,
)
.map_err(|e| other_err!("client-to-server", source: e))?;
per::write_length(dst, cast_length!("gccBlocksLen", gcc_blocks_buffer_length)?);
self.gcc_blocks.encode(dst)?;
Ok(())
}
fn name(&self) -> &'static str {
Self::NAME
}
fn size(&self) -> usize {
let gcc_blocks_buffer_length = self.gcc_blocks.size();
let req_length: DecodeResult<u16> = cast_length!(
"gccBlocksLen",
CONFERENCE_REQUEST_CONNECT_PDU_SIZE + gcc_blocks_buffer_length
);
let length: DecodeResult<u16> = cast_length!("gccBlocksLen", gcc_blocks_buffer_length);
per::CHOICE_SIZE
+ CONFERENCE_REQUEST_OBJECT_ID.len()
+ per::sizeof_length(req_length.unwrap())
+ CONFERENCE_REQUEST_CONNECT_PDU_SIZE
+ per::sizeof_length(length.unwrap())
+ gcc_blocks_buffer_length
}
}
impl<'de> Decode<'de> for ConferenceCreateRequest {
fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_choice(src) != OBJECT_IDENTIFIER_KEY {
return Err(invalid_field_err!("ConnectData::Key", "Got unexpected ConnectData key"));
}
if per::read_object_id(src).map_err(|e| other_err!("value", source: e))? != CONFERENCE_REQUEST_OBJECT_ID {
return Err(invalid_field_err!(
"ConnectData::Key",
"Got unexpected ConnectData key value"
));
}
let _length = per::read_length(src).map_err(|e| other_err!("len", source: e))?;
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_choice(src) != CONNECT_GCC_PDU_CONFERENCE_REQUEST_CHOICE {
return Err(invalid_field_err!(
"ConnectData::connectPdu",
"Got invalid ConnectGCCPDU choice (expected ConferenceCreateRequest)"
));
}
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_selection(src) != CONFERENCE_REQUEST_USER_DATA_SELECTION {
return Err(invalid_field_err!(
"ConferenceCreateRequest::Selection",
"Got invalid ConferenceCreateRequest selection (expected UserData)",
));
}
per::read_numeric_string(src, 1).map_err(|e| other_err!("confName", source: e))?;
per::read_padding(src, 1);
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_number_of_sets(src) != USER_DATA_NUMBER_OF_SETS {
return Err(invalid_field_err!(
"ConferenceCreateRequest",
"Got invalid ConferenceCreateRequest number of sets (expected 1)",
));
}
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_choice(src) != USER_DATA_H221_NON_STANDARD_CHOICE {
return Err(invalid_field_err!(
"ConferenceCreateRequest",
"Expected UserData H221NonStandard choice",
));
}
if per::read_octet_string(src, H221_NON_STANDARD_MIN_LENGTH)
.map_err(|e| other_err!("client-to-server", source: e))?
!= CONFERENCE_REQUEST_CLIENT_TO_SERVER_H221_NON_STANDARD
{
return Err(invalid_field_err!(
"ConferenceCreateRequest",
"Got invalid H221NonStandard client-to-server key",
));
}
let (_gcc_blocks_buffer_length, _) = per::read_length(src).map_err(|e| other_err!("len", source: e))?;
let gcc_blocks = ClientGccBlocks::decode(src)?;
Ok(Self { gcc_blocks })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConferenceCreateResponse {
pub user_id: u16,
pub gcc_blocks: ServerGccBlocks,
}
impl ConferenceCreateResponse {
const NAME: &'static str = "ConferenceCreateResponse";
}
impl Encode for ConferenceCreateResponse {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
let gcc_blocks_buffer_length = self.gcc_blocks.size();
per::write_choice(dst, OBJECT_IDENTIFIER_KEY);
per::write_object_id(dst, CONFERENCE_REQUEST_OBJECT_ID);
per::write_length(
dst,
cast_length!(
"gccBlocksLen",
gcc_blocks_buffer_length + CONFERENCE_RESPONSE_CONNECT_PDU_SIZE + 1
)?,
);
per::write_choice(dst, CONNECT_GCC_PDU_CONFERENCE_RESPONSE_CHOICE);
per::write_u16(dst, self.user_id, CONFERENCE_REQUEST_U16_MIN).map_err(|e| other_err!("userId", source: e))?;
per::write_u32(dst, CONFERENCE_RESPONSE_TAG);
per::write_enum(dst, CONFERENCE_RESPONSE_RESULT);
per::write_number_of_sets(dst, USER_DATA_NUMBER_OF_SETS);
per::write_choice(dst, USER_DATA_H221_NON_STANDARD_CHOICE);
per::write_octet_string(
dst,
CONFERENCE_REQUEST_SERVER_TO_CLIENT_H221_NON_STANDARD,
H221_NON_STANDARD_MIN_LENGTH,
)
.map_err(|e| other_err!("server-to-client", source: e))?;
per::write_length(dst, gcc_blocks_buffer_length as u16);
self.gcc_blocks.encode(dst)?;
Ok(())
}
fn name(&self) -> &'static str {
Self::NAME
}
fn size(&self) -> usize {
let gcc_blocks_buffer_length = self.gcc_blocks.size();
let req_length: DecodeResult<u16> = cast_length!(
"gccBlocksLen",
CONFERENCE_RESPONSE_CONNECT_PDU_SIZE + gcc_blocks_buffer_length
);
let length: DecodeResult<u16> = cast_length!("gccBlocksLen", gcc_blocks_buffer_length);
per::CHOICE_SIZE
+ CONFERENCE_REQUEST_OBJECT_ID.len()
+ per::sizeof_length(req_length.unwrap())
+ CONFERENCE_RESPONSE_CONNECT_PDU_SIZE
+ per::sizeof_length(length.unwrap())
+ gcc_blocks_buffer_length
}
}
impl<'de> Decode<'de> for ConferenceCreateResponse {
fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_choice(src) != OBJECT_IDENTIFIER_KEY {
return Err(invalid_field_err!("ConnectData::Key", "Got unexpected ConnectData key"));
}
if per::read_object_id(src).map_err(|e| other_err!("value", source: e))? != CONFERENCE_REQUEST_OBJECT_ID {
return Err(invalid_field_err!(
"ConnectData::Key",
"Got unexpected ConnectData key value"
));
};
let _length = per::read_length(src).map_err(|e| other_err!("len", source: e))?;
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_choice(src) != CONNECT_GCC_PDU_CONFERENCE_RESPONSE_CHOICE {
return Err(invalid_field_err!(
"ConnectData::connectPdu",
"Got invalid ConnectGCCPDU choice (expected ConferenceCreateResponse)"
));
}
let user_id = per::read_u16(src, CONFERENCE_REQUEST_U16_MIN).map_err(|e| other_err!("userId", source: e))?;
if per::read_u32(src).map_err(|e| other_err!("tag", source: e))? != CONFERENCE_RESPONSE_TAG {
return Err(invalid_field_err!(
"ConferenceCreateResponse::tag",
"Got unexpected ConferenceCreateResponse tag",
));
}
if per::read_enum(src, mcs::RESULT_ENUM_LENGTH).map_err(|e| other_err!("result", source: e))?
!= CONFERENCE_RESPONSE_RESULT
{
return Err(invalid_field_err!(
"ConferenceCreateResponse::result",
"Got invalid ConferenceCreateResponse result",
));
}
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_number_of_sets(src) != USER_DATA_NUMBER_OF_SETS {
return Err(invalid_field_err!(
"ConferenceCreateResponse",
"Got invalid ConferenceCreateResponse number of sets (expected 1)",
));
}
ensure_size!(in: src, size: per::CHOICE_SIZE);
if per::read_choice(src) != USER_DATA_H221_NON_STANDARD_CHOICE {
return Err(invalid_field_err!(
"ConferenceCreateResponse",
"Expected UserData H221NonStandard choice",
));
}
if per::read_octet_string(src, H221_NON_STANDARD_MIN_LENGTH)
.map_err(|e| other_err!("server-to-client", source: e))?
!= CONFERENCE_REQUEST_SERVER_TO_CLIENT_H221_NON_STANDARD
{
return Err(invalid_field_err!(
"ConferenceCreateResponse",
"Got invalid H221NonStandard server-to-client key",
));
}
let (_gcc_blocks_buffer_length, _) = per::read_length(src).map_err(|e| other_err!("len", source: e))?;
let gcc_blocks = ServerGccBlocks::decode(src)?;
Ok(Self { user_id, gcc_blocks })
}
}