feat: initial server support (#167)

This commit is contained in:
Mihnea Buzatu
2023-08-25 09:35:00 -04:00
committed by GitHub
parent 1132126c2a
commit 74e95f692a
32 changed files with 3205 additions and 236 deletions
Generated
+28
View File
@@ -1704,13 +1704,25 @@ checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
name = "ironrdp"
version = "0.5.0"
dependencies = [
"ironrdp-acceptor",
"ironrdp-connector",
"ironrdp-graphics",
"ironrdp-input",
"ironrdp-pdu",
"ironrdp-server",
"ironrdp-session",
]
[[package]]
name = "ironrdp-acceptor"
version = "0.1.0"
dependencies = [
"ironrdp-async",
"ironrdp-connector",
"ironrdp-pdu",
"tracing",
]
[[package]]
name = "ironrdp-async"
version = "0.1.0"
@@ -1856,6 +1868,22 @@ dependencies = [
"der 0.7.8",
]
[[package]]
name = "ironrdp-server"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"bytes",
"ironrdp-acceptor",
"ironrdp-graphics",
"ironrdp-pdu",
"ironrdp-tokio",
"tokio",
"tokio-rustls 0.24.0",
"tracing",
]
[[package]]
name = "ironrdp-session"
version = "0.1.0"
+2
View File
@@ -25,6 +25,7 @@ expect-test = "1"
ironrdp-async = { version = "0.1", path = "crates/ironrdp-async" }
ironrdp-cliprdr = { version = "0.1", path = "crates/ironrdp-cliprdr" }
ironrdp-connector = { version = "0.1", path = "crates/ironrdp-connector" }
ironrdp-acceptor = { version = "0.1", path = "crates/ironrdp-acceptor" }
ironrdp-error = { version = "0.1", path = "crates/ironrdp-error" }
ironrdp-futures = { version = "0.1", path = "crates/ironrdp-futures" }
ironrdp-fuzzing = { path = "crates/ironrdp-fuzzing" }
@@ -38,6 +39,7 @@ ironrdp-session = { version = "0.1", path = "crates/ironrdp-session" }
ironrdp-testsuite-core = { path = "crates/ironrdp-testsuite-core" }
ironrdp-tls = { version = "0.1", path = "crates/ironrdp-tls" }
ironrdp-tokio = { version = "0.1", path = "crates/ironrdp-tokio" }
ironrdp-server = { version = "0.1", path = "crates/ironrdp-server" }
ironrdp = { version = "0.5", path = "crates/ironrdp" }
proptest = "1.1.0"
rstest = "0.17.0"
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "ironrdp-acceptor"
version = "0.1.0"
readme = "README.md"
description = ""
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
authors.workspace = true
keywords.workspace = true
categories.workspace = true
[lib]
doctest = false
test = false
[dependencies]
ironrdp-pdu.workspace = true
ironrdp-connector.workspace = true
ironrdp-async.workspace = true
tracing.workspace = true
+3
View File
@@ -0,0 +1,3 @@
# IronRDP Acceptor
State machine for the server connection acceptance sequence.
@@ -0,0 +1,168 @@
use std::collections::HashSet;
use ironrdp_connector::{ConnectorError, ConnectorErrorExt, ConnectorResult, Sequence, State, Written};
use ironrdp_pdu as pdu;
use pdu::mcs;
#[derive(Debug)]
pub struct ChannelConnectionSequence {
state: ChannelConnectionState,
user_channel_id: u16,
channels: HashSet<u16>,
}
#[derive(Default, Debug)]
pub enum ChannelConnectionState {
#[default]
Consumed,
WaitErectDomainRequest,
WaitAttachUserRequest,
SendAttachUserConfirm,
WaitChannelJoinRequest {
joined: HashSet<u16>,
},
SendChannelJoinConfirm {
joined: HashSet<u16>,
channel_id: u16,
},
AllJoined,
}
impl State for ChannelConnectionState {
fn name(&self) -> &'static str {
match self {
Self::Consumed => "Consumed",
Self::WaitErectDomainRequest => "WaitErectDomainRequest",
Self::WaitAttachUserRequest => "WaitAttachUserRequest",
Self::SendAttachUserConfirm => "SendAttachUserConfirm",
Self::WaitChannelJoinRequest { .. } => "WaitChannelJoinRequest",
Self::SendChannelJoinConfirm { .. } => "SendChannelJoinConfirm",
Self::AllJoined { .. } => "AllJoined",
}
}
fn is_terminal(&self) -> bool {
matches!(self, Self::AllJoined { .. })
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
impl Sequence for ChannelConnectionSequence {
fn next_pdu_hint(&self) -> Option<&dyn pdu::PduHint> {
match &self.state {
ChannelConnectionState::Consumed => None,
ChannelConnectionState::WaitErectDomainRequest => Some(&pdu::X224_HINT),
ChannelConnectionState::WaitAttachUserRequest => Some(&pdu::X224_HINT),
ChannelConnectionState::SendAttachUserConfirm => None,
ChannelConnectionState::WaitChannelJoinRequest { .. } => Some(&pdu::X224_HINT),
ChannelConnectionState::SendChannelJoinConfirm { .. } => None,
ChannelConnectionState::AllJoined { .. } => None,
}
}
fn state(&self) -> &dyn State {
&self.state
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match std::mem::take(&mut self.state) {
ChannelConnectionState::WaitErectDomainRequest => {
let erect_domain_request =
ironrdp_pdu::decode::<mcs::ErectDomainPdu>(input).map_err(ConnectorError::pdu)?;
debug!(message = ?erect_domain_request, "Received");
(Written::Nothing, ChannelConnectionState::WaitAttachUserRequest)
}
ChannelConnectionState::WaitAttachUserRequest => {
let attach_user_request =
ironrdp_pdu::decode::<mcs::AttachUserRequest>(input).map_err(ConnectorError::pdu)?;
debug!(message = ?attach_user_request, "Received");
(Written::Nothing, ChannelConnectionState::SendAttachUserConfirm)
}
ChannelConnectionState::SendAttachUserConfirm => {
let attach_user_confirm = mcs::AttachUserConfirm {
result: 0,
initiator_id: self.user_channel_id,
};
debug!(message = ?attach_user_confirm, "Send");
let written = ironrdp_pdu::encode_buf(&attach_user_confirm, output).map_err(ConnectorError::pdu)?;
(
Written::from_size(written)?,
ChannelConnectionState::WaitChannelJoinRequest { joined: HashSet::new() },
)
}
// TODO: support RNS_UD_CS_SUPPORT_SKIP_CHANNELJOIN
ChannelConnectionState::WaitChannelJoinRequest { joined } => {
let channel_request =
ironrdp_pdu::decode::<mcs::ChannelJoinRequest>(input).map_err(ConnectorError::pdu)?;
debug!(message = ?channel_request, "Received");
let channel_id = channel_request.channel_id;
(
Written::Nothing,
ChannelConnectionState::SendChannelJoinConfirm { joined, channel_id },
)
}
ChannelConnectionState::SendChannelJoinConfirm { mut joined, channel_id } => {
let channel_confirm = mcs::ChannelJoinConfirm {
result: 0,
initiator_id: self.user_channel_id,
requested_channel_id: channel_id,
channel_id,
};
debug!(message = ?channel_confirm, "Send");
let written = ironrdp_pdu::encode_buf(&channel_confirm, output).map_err(ConnectorError::pdu)?;
joined.insert(channel_id);
let state = if joined != self.channels {
ChannelConnectionState::WaitChannelJoinRequest { joined }
} else {
ChannelConnectionState::AllJoined {}
};
(Written::from_size(written)?, state)
}
_ => unreachable!(),
};
self.state = next_state;
Ok(written)
}
}
impl ChannelConnectionSequence {
pub fn new(user_channel_id: u16, io_channel_id: u16, other_channels: Vec<u16>) -> Self {
Self {
state: ChannelConnectionState::WaitErectDomainRequest,
user_channel_id,
channels: vec![user_channel_id, io_channel_id]
.into_iter()
.chain(other_channels.into_iter())
.collect(),
}
}
pub fn is_done(&self) -> bool {
self.state.is_terminal()
}
}
+494
View File
@@ -0,0 +1,494 @@
use std::io::Cursor;
use ironrdp_connector::{
legacy, ConnectorError, ConnectorErrorExt, ConnectorResult, DesktopSize, Sequence, State, Written,
};
use ironrdp_pdu as pdu;
use pdu::rdp::capability_sets::CapabilitySet;
use pdu::rdp::headers::ShareControlPdu;
use pdu::{gcc, mcs, nego, rdp, PduParsing};
use crate::util::{self, wrap_share_data};
use super::channel_connection::ChannelConnectionSequence;
use super::finalization::FinalizationSequence;
const IO_CHANNEL_ID: u16 = 1003;
const USER_CHANNEL_ID: u16 = 1002;
pub struct Acceptor {
state: AcceptorState,
security: nego::SecurityProtocol,
io_channel_id: u16,
user_channel_id: u16,
desktop_size: DesktopSize,
server_capabilities: Vec<CapabilitySet>,
}
#[derive(Debug, Clone)]
pub struct AcceptorResult {
pub channels: Vec<(u16, gcc::Channel)>,
pub capabilities: Vec<CapabilitySet>,
}
impl Acceptor {
pub fn new(security: nego::SecurityProtocol, desktop_size: DesktopSize, capabilities: Vec<CapabilitySet>) -> Self {
Self {
security,
state: AcceptorState::InitiationWaitRequest,
user_channel_id: USER_CHANNEL_ID,
io_channel_id: IO_CHANNEL_ID,
desktop_size,
server_capabilities: capabilities,
}
}
pub fn reached_security_upgrade(&self) -> Option<nego::SecurityProtocol> {
match self.state {
AcceptorState::SecurityUpgrade { .. } => Some(self.security),
_ => None,
}
}
pub fn get_result(&mut self) -> Option<AcceptorResult> {
match &self.state {
AcceptorState::Accepted {
channels,
client_capabilities,
} => Some(AcceptorResult {
channels: channels.clone(),
capabilities: client_capabilities.clone(),
}),
_ => None,
}
}
}
#[derive(Default, Debug)]
pub enum AcceptorState {
#[default]
Consumed,
InitiationWaitRequest,
InitiationSendConfirm {
requested_protocol: nego::SecurityProtocol,
},
SecurityUpgrade {
requested_protocol: nego::SecurityProtocol,
},
BasicSettingsWaitInitial {
requested_protocol: nego::SecurityProtocol,
},
BasicSettingsSendResponse {
requested_protocol: nego::SecurityProtocol,
early_capability: Option<gcc::ClientEarlyCapabilityFlags>,
channels: Vec<(u16, gcc::Channel)>,
},
ChannelConnection {
early_capability: Option<gcc::ClientEarlyCapabilityFlags>,
channels: Vec<(u16, gcc::Channel)>,
connection: ChannelConnectionSequence,
},
RdpSecurityCommencement {
early_capability: Option<gcc::ClientEarlyCapabilityFlags>,
channels: Vec<(u16, gcc::Channel)>,
},
SecureSettingsExchange {
early_capability: Option<gcc::ClientEarlyCapabilityFlags>,
channels: Vec<(u16, gcc::Channel)>,
},
LicensingExchange {
early_capability: Option<gcc::ClientEarlyCapabilityFlags>,
channels: Vec<(u16, gcc::Channel)>,
},
CapabilitiesSendServer {
early_capability: Option<gcc::ClientEarlyCapabilityFlags>,
channels: Vec<(u16, gcc::Channel)>,
},
MonitorLayoutSend {
channels: Vec<(u16, gcc::Channel)>,
},
CapabilitiesWaitConfirm {
channels: Vec<(u16, gcc::Channel)>,
},
ConnectionFinalization {
finalization: FinalizationSequence,
channels: Vec<(u16, gcc::Channel)>,
client_capabilities: Vec<CapabilitySet>,
},
Accepted {
channels: Vec<(u16, gcc::Channel)>,
client_capabilities: Vec<CapabilitySet>,
},
}
impl State for AcceptorState {
fn name(&self) -> &'static str {
match self {
Self::Consumed => "Consumed",
Self::InitiationWaitRequest => "InitiationWaitRequest",
Self::InitiationSendConfirm { .. } => "InitiationSendConfirm",
Self::SecurityUpgrade { .. } => "SecurityUpgrade",
Self::BasicSettingsWaitInitial { .. } => "BasicSettingsWaitInitial",
Self::BasicSettingsSendResponse { .. } => "BasicSettingsSendResponse",
Self::ChannelConnection { .. } => "ChannelConnection",
Self::RdpSecurityCommencement { .. } => "RdpSecurityCommencement",
Self::SecureSettingsExchange { .. } => "SecureSettingsExchange",
Self::LicensingExchange { .. } => "LicensingExchange",
Self::CapabilitiesSendServer { .. } => "CapabilitiesSendServer",
Self::MonitorLayoutSend { .. } => "MonitorLayoutSend",
Self::CapabilitiesWaitConfirm { .. } => "CapabilitiesWaitConfirm",
Self::ConnectionFinalization { .. } => "ConnectionFinalization",
Self::Accepted { .. } => "Connected",
}
}
fn is_terminal(&self) -> bool {
matches!(self, Self::Accepted { .. })
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
impl Sequence for Acceptor {
fn next_pdu_hint(&self) -> Option<&dyn pdu::PduHint> {
match &self.state {
AcceptorState::Consumed => None,
AcceptorState::InitiationWaitRequest => Some(&pdu::X224_HINT),
AcceptorState::InitiationSendConfirm { .. } => None,
AcceptorState::SecurityUpgrade { .. } => None,
AcceptorState::BasicSettingsWaitInitial { .. } => Some(&pdu::X224_HINT),
AcceptorState::BasicSettingsSendResponse { .. } => None,
AcceptorState::ChannelConnection { connection, .. } => connection.next_pdu_hint(),
AcceptorState::RdpSecurityCommencement { .. } => None,
AcceptorState::SecureSettingsExchange { .. } => Some(&pdu::X224_HINT),
AcceptorState::LicensingExchange { .. } => None,
AcceptorState::CapabilitiesSendServer { .. } => None,
AcceptorState::MonitorLayoutSend { .. } => None,
AcceptorState::CapabilitiesWaitConfirm { .. } => Some(&pdu::X224_HINT),
AcceptorState::ConnectionFinalization { finalization, .. } => finalization.next_pdu_hint(),
AcceptorState::Accepted { .. } => None,
}
}
fn state(&self) -> &dyn State {
&self.state
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match std::mem::take(&mut self.state) {
AcceptorState::InitiationWaitRequest => {
let connection_request =
ironrdp_pdu::decode::<nego::ConnectionRequest>(input).map_err(ConnectorError::pdu)?;
debug!(message = ?connection_request, "Received");
(
Written::Nothing,
AcceptorState::InitiationSendConfirm {
requested_protocol: connection_request.protocol,
},
)
}
AcceptorState::InitiationSendConfirm { requested_protocol } => {
let connection_confirm = nego::ConnectionConfirm::Response {
flags: nego::ResponseFlags::empty(),
protocol: self.security,
};
debug!(message = ?connection_confirm, "Send");
let written = ironrdp_pdu::encode_buf(&connection_confirm, output).map_err(ConnectorError::pdu)?;
(
Written::from_size(written)?,
AcceptorState::SecurityUpgrade { requested_protocol },
)
}
AcceptorState::SecurityUpgrade { requested_protocol } => (
Written::Nothing,
AcceptorState::BasicSettingsWaitInitial { requested_protocol },
),
AcceptorState::BasicSettingsWaitInitial { requested_protocol } => {
let settings_initial = legacy::decode_x224_packet::<mcs::ConnectInitial>(input)?;
debug!(message = ?settings_initial, "Received");
let early_capability = settings_initial
.conference_create_request
.gcc_blocks
.core
.optional_data
.early_capability_flags;
let channels = settings_initial
.conference_create_request
.gcc_blocks
.network
.map(|network| {
network
.channels
.into_iter()
.enumerate()
.map(|(i, c)| (i as u16 + self.io_channel_id + 1, c))
.collect()
})
.unwrap_or(Vec::new());
(
Written::Nothing,
AcceptorState::BasicSettingsSendResponse {
requested_protocol,
early_capability,
channels,
},
)
}
AcceptorState::BasicSettingsSendResponse {
requested_protocol,
early_capability,
channels,
} => {
let channel_ids: Vec<u16> = channels.iter().map(|&(i, _)| i).collect();
let server_blocks = create_gcc_blocks(self.io_channel_id, channel_ids.clone(), requested_protocol);
let settings_response = mcs::ConnectResponse {
conference_create_response: gcc::ConferenceCreateResponse {
user_id: self.user_channel_id,
gcc_blocks: server_blocks,
},
called_connect_id: 1,
domain_parameters: mcs::DomainParameters::target(),
};
debug!(message = ?settings_response, "Send");
let written = legacy::encode_x224_packet(&settings_response, output)?;
(
Written::from_size(written)?,
AcceptorState::ChannelConnection {
early_capability,
channels,
connection: ChannelConnectionSequence::new(
self.user_channel_id,
self.io_channel_id,
channel_ids,
),
},
)
}
AcceptorState::ChannelConnection {
early_capability,
channels,
mut connection,
} => {
let written = connection.step(input, output)?;
let state = if connection.is_done() {
AcceptorState::RdpSecurityCommencement {
early_capability,
channels,
}
} else {
AcceptorState::ChannelConnection {
early_capability,
channels,
connection,
}
};
(written, state)
}
AcceptorState::RdpSecurityCommencement {
early_capability,
channels,
..
} => (
Written::Nothing,
AcceptorState::SecureSettingsExchange {
early_capability,
channels,
},
),
AcceptorState::SecureSettingsExchange {
early_capability,
channels,
} => {
let data = pdu::decode::<pdu::mcs::SendDataRequest>(input).map_err(ConnectorError::pdu)?;
let client_info = rdp::ClientInfoPdu::from_buffer(Cursor::new(data.user_data))?;
debug!(message = ?client_info, "Received");
(
Written::Nothing,
AcceptorState::LicensingExchange {
early_capability,
channels,
},
)
}
AcceptorState::LicensingExchange {
early_capability,
channels,
} => {
let license = rdp::server_license::InitialServerLicenseMessage::new_status_valid_client_message();
debug!(message = ?license, "Send");
let written =
util::encode_send_data_indication(self.user_channel_id, self.io_channel_id, &license, output)?;
(
Written::from_size(written)?,
AcceptorState::CapabilitiesSendServer {
early_capability,
channels,
},
)
}
AcceptorState::CapabilitiesSendServer {
early_capability,
channels,
} => {
let demand_active = rdp::headers::ShareControlHeader {
share_id: 0,
pdu_source: self.io_channel_id,
share_control_pdu: rdp::headers::ShareControlPdu::ServerDemandActive(
rdp::capability_sets::ServerDemandActive {
pdu: rdp::capability_sets::DemandActive {
source_descriptor: "".into(),
capability_sets: self.server_capabilities.clone(),
},
},
),
};
debug!(message = ?demand_active, "Send");
let written = util::encode_send_data_indication(
self.user_channel_id,
self.io_channel_id,
&demand_active,
output,
)?;
let layout_flag = gcc::ClientEarlyCapabilityFlags::SUPPORT_MONITOR_LAYOUT_PDU;
let next_state = if early_capability.is_some_and(|c| c.contains(layout_flag)) {
AcceptorState::MonitorLayoutSend { channels }
} else {
AcceptorState::CapabilitiesWaitConfirm { channels }
};
(Written::from_size(written)?, next_state)
}
AcceptorState::MonitorLayoutSend { channels } => {
let monitor_layout =
rdp::headers::ShareDataPdu::MonitorLayout(rdp::finalization_messages::MonitorLayoutPdu {
monitors: vec![gcc::Monitor {
left: 0,
top: 0,
right: self.desktop_size.width as i32,
bottom: self.desktop_size.height as i32,
flags: gcc::MonitorFlags::PRIMARY,
}],
});
debug!(message = ?monitor_layout, "Send");
let share_data = wrap_share_data(monitor_layout, self.io_channel_id);
let written =
util::encode_send_data_indication(self.user_channel_id, self.io_channel_id, &share_data, output)?;
(
Written::from_size(written)?,
AcceptorState::CapabilitiesWaitConfirm { channels },
)
}
AcceptorState::CapabilitiesWaitConfirm { channels } => {
let data = pdu::decode::<pdu::mcs::SendDataRequest>(input).map_err(ConnectorError::pdu)?;
let capabilities_confirm = rdp::headers::ShareControlHeader::from_buffer(Cursor::new(data.user_data))?;
debug!(message = ?capabilities_confirm, "Received");
let ShareControlPdu::ClientConfirmActive(confirm) = capabilities_confirm.share_control_pdu else {
return Err(ConnectorError::general("expected client confirm active"));
};
(
Written::Nothing,
AcceptorState::ConnectionFinalization {
channels,
finalization: FinalizationSequence::new(self.user_channel_id, self.io_channel_id),
client_capabilities: confirm.pdu.capability_sets,
},
)
}
AcceptorState::ConnectionFinalization {
mut finalization,
channels,
client_capabilities,
} => {
let written = finalization.step(input, output)?;
let state = if finalization.is_done() {
AcceptorState::Accepted {
channels,
client_capabilities,
}
} else {
AcceptorState::ConnectionFinalization {
finalization,
channels,
client_capabilities,
}
};
(written, state)
}
_ => unreachable!(),
};
self.state = next_state;
Ok(written)
}
}
fn create_gcc_blocks(
io_channel: u16,
channel_ids: Vec<u16>,
requested: nego::SecurityProtocol,
) -> gcc::ServerGccBlocks {
pdu::gcc::ServerGccBlocks {
core: gcc::ServerCoreData {
version: gcc::RdpVersion::V5_PLUS,
optional_data: gcc::ServerCoreOptionalData {
client_requested_protocols: Some(requested),
early_capability_flags: None,
},
},
security: gcc::ServerSecurityData::no_security(),
network: gcc::ServerNetworkData {
channel_ids,
io_channel,
},
message_channel: None,
multi_transport_channel: None,
}
}
+224
View File
@@ -0,0 +1,224 @@
use std::io::Cursor;
use ironrdp_connector::{ConnectorError, ConnectorErrorExt, ConnectorResult, Sequence, State, Written};
use ironrdp_pdu as pdu;
use pdu::{rdp, PduParsing};
use crate::util::{self, wrap_share_data};
#[derive(Debug)]
pub struct FinalizationSequence {
state: FinalizationState,
user_channel_id: u16,
io_channel_id: u16,
}
#[derive(Default, Debug)]
pub enum FinalizationState {
#[default]
Consumed,
WaitSynchronize,
WaitControlCooperate,
WaitRequestControl,
WaitFontList,
SendSynchronizeConfirm,
SendControlCooperateConfirm,
SendGrantedControlConfirm,
SendFontMap,
Finished,
}
impl State for FinalizationState {
fn name(&self) -> &'static str {
match self {
Self::Consumed => "Consumed",
Self::WaitSynchronize => "WaitSynchronize",
Self::WaitControlCooperate => "WaitControlCooperate",
Self::WaitRequestControl => "WaitRequestControl",
Self::WaitFontList => "WaitFontList",
Self::SendSynchronizeConfirm => "SendSynchronizeConfirm",
Self::SendControlCooperateConfirm => "SendControlCooperateConfirm",
Self::SendGrantedControlConfirm => "SendGrantedControlConfirm",
Self::SendFontMap => "SendFontMap",
Self::Finished => "Finished",
}
}
fn is_terminal(&self) -> bool {
matches!(self, Self::Finished { .. })
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
impl Sequence for FinalizationSequence {
fn next_pdu_hint(&self) -> Option<&dyn pdu::PduHint> {
match &self.state {
FinalizationState::Consumed => None,
FinalizationState::WaitSynchronize => Some(&pdu::X224Hint),
FinalizationState::WaitControlCooperate => Some(&pdu::X224Hint),
FinalizationState::WaitRequestControl => Some(&pdu::X224Hint),
FinalizationState::WaitFontList => Some(&pdu::X224Hint),
FinalizationState::SendSynchronizeConfirm => None,
FinalizationState::SendControlCooperateConfirm => None,
FinalizationState::SendGrantedControlConfirm => None,
FinalizationState::SendFontMap => None,
FinalizationState::Finished => None,
}
}
fn state(&self) -> &dyn State {
&self.state
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match std::mem::take(&mut self.state) {
FinalizationState::WaitSynchronize => {
let data = pdu::decode::<pdu::mcs::SendDataRequest>(input).map_err(ConnectorError::pdu)?;
let synchronize = rdp::headers::ShareControlHeader::from_buffer(Cursor::new(data.user_data))?;
debug!(message = ?synchronize, "Received");
(Written::Nothing, FinalizationState::WaitControlCooperate)
}
FinalizationState::WaitControlCooperate => {
let data = pdu::decode::<pdu::mcs::SendDataRequest>(input).map_err(ConnectorError::pdu)?;
let cooperate = rdp::headers::ShareControlHeader::from_buffer(Cursor::new(data.user_data))?;
debug!(message = ?cooperate, "Received");
(Written::Nothing, FinalizationState::WaitRequestControl)
}
FinalizationState::WaitRequestControl => {
let data = pdu::decode::<pdu::mcs::SendDataRequest>(input).map_err(ConnectorError::pdu)?;
let control = rdp::headers::ShareControlHeader::from_buffer(Cursor::new(data.user_data))?;
debug!(message = ?control, "Received");
(Written::Nothing, FinalizationState::WaitFontList)
}
FinalizationState::WaitFontList => {
let data = pdu::decode::<pdu::mcs::SendDataRequest>(input).map_err(ConnectorError::pdu)?;
let font_list = rdp::headers::ShareControlHeader::from_buffer(Cursor::new(data.user_data))?;
debug!(message = ?font_list, "Received");
(Written::Nothing, FinalizationState::SendSynchronizeConfirm)
}
FinalizationState::SendSynchronizeConfirm => {
let synchronize_confirm = create_synchronize_confirm();
debug!(message = ?synchronize_confirm, "Send");
let share_data = wrap_share_data(synchronize_confirm, self.io_channel_id);
let written =
util::encode_send_data_indication(self.user_channel_id, self.io_channel_id, &share_data, output)?;
(
Written::from_size(written)?,
FinalizationState::SendControlCooperateConfirm,
)
}
FinalizationState::SendControlCooperateConfirm => {
let cooperate_confirm = create_cooperate_confirm();
debug!(message = ?cooperate_confirm, "Send");
let share_data = wrap_share_data(cooperate_confirm, self.io_channel_id);
let written =
util::encode_send_data_indication(self.user_channel_id, self.io_channel_id, &share_data, output)?;
(
Written::from_size(written)?,
FinalizationState::SendGrantedControlConfirm,
)
}
FinalizationState::SendGrantedControlConfirm => {
let control_confirm = create_control_confirm(self.user_channel_id);
debug!(message = ?control_confirm, "Send");
let share_data = wrap_share_data(control_confirm, self.io_channel_id);
let written =
util::encode_send_data_indication(self.user_channel_id, self.io_channel_id, &share_data, output)?;
(Written::from_size(written)?, FinalizationState::SendFontMap)
}
FinalizationState::SendFontMap => {
let font_map = create_font_map();
debug!(message = ?font_map, "Send");
let share_data = wrap_share_data(font_map, self.io_channel_id);
let written =
util::encode_send_data_indication(self.user_channel_id, self.io_channel_id, &share_data, output)?;
(Written::from_size(written)?, FinalizationState::Finished)
}
_ => unreachable!(),
};
self.state = next_state;
Ok(written)
}
}
impl FinalizationSequence {
pub fn new(user_channel_id: u16, io_channel_id: u16) -> Self {
Self {
state: FinalizationState::WaitSynchronize,
user_channel_id,
io_channel_id,
}
}
pub fn is_done(&self) -> bool {
self.state.is_terminal()
}
}
fn create_synchronize_confirm() -> rdp::headers::ShareDataPdu {
rdp::headers::ShareDataPdu::Synchronize(rdp::finalization_messages::SynchronizePdu { target_user_id: 0 })
}
fn create_cooperate_confirm() -> rdp::headers::ShareDataPdu {
rdp::headers::ShareDataPdu::Control(rdp::finalization_messages::ControlPdu {
action: rdp::finalization_messages::ControlAction::Cooperate,
grant_id: 0,
control_id: 0,
})
}
fn create_control_confirm(user_id: u16) -> rdp::headers::ShareDataPdu {
rdp::headers::ShareDataPdu::Control(rdp::finalization_messages::ControlPdu {
action: rdp::finalization_messages::ControlAction::GrantedControl,
grant_id: user_id,
control_id: u32::from(pdu::rdp::capability_sets::SERVER_CHANNEL_ID),
})
}
fn create_font_map() -> rdp::headers::ShareDataPdu {
rdp::headers::ShareDataPdu::FontMap(rdp::finalization_messages::FontPdu {
number: 1, // TODO: fields
total_number: 1,
flags: rdp::finalization_messages::SequenceFlags::empty(),
entry_size: 0,
})
}
+98
View File
@@ -0,0 +1,98 @@
#[macro_use]
extern crate tracing;
use ironrdp_async::{Framed, FramedRead, FramedWrite, StreamWrapper};
use ironrdp_connector::{custom_err, ConnectorResult, Sequence, Written};
mod channel_connection;
mod connection;
mod finalization;
mod util;
pub use connection::{Acceptor, AcceptorResult};
pub use ironrdp_connector::DesktopSize;
pub enum BeginResult<S>
where
S: StreamWrapper,
{
ShouldUpgrade(S::InnerStream),
Continue(Framed<S>),
}
pub async fn accept_begin<S>(mut framed: Framed<S>, acceptor: &mut Acceptor) -> ConnectorResult<BeginResult<S>>
where
S: FramedRead + FramedWrite + StreamWrapper,
{
let mut buf = Vec::new();
loop {
if let Some(security) = acceptor.reached_security_upgrade() {
let result = if security.is_empty() {
BeginResult::Continue(framed)
} else {
BeginResult::ShouldUpgrade(framed.into_inner_no_leftover())
};
return Ok(result);
}
single_accept_state(&mut framed, acceptor, &mut buf).await?;
}
}
pub async fn accept_finalize<S>(
mut framed: Framed<S>,
acceptor: &mut Acceptor,
) -> ConnectorResult<(Framed<S>, AcceptorResult)>
where
S: FramedRead + FramedWrite,
{
let mut buf = Vec::new();
loop {
if let Some(result) = acceptor.get_result() {
return Ok((framed, result));
}
single_accept_state(&mut framed, acceptor, &mut buf).await?;
}
}
async fn single_accept_state<S>(
framed: &mut Framed<S>,
acceptor: &mut Acceptor,
buf: &mut Vec<u8>,
) -> ConnectorResult<Written>
where
S: FramedRead + FramedWrite,
{
let written = if let Some(next_pdu_hint) = acceptor.next_pdu_hint() {
debug!(
acceptor.state = acceptor.state().name(),
hint = ?next_pdu_hint,
"Wait for PDU"
);
let pdu = framed
.read_by_hint(next_pdu_hint)
.await
.map_err(|e| custom_err!("read frame by hint", e))?;
trace!(length = pdu.len(), "PDU received");
acceptor.step(&pdu, buf)?
} else {
acceptor.step_no_input(buf)?
};
if let Some(len) = written.size() {
trace!(length = len, "Send response");
framed
.write_all(&buf[..len])
.await
.map_err(|e| custom_err!("write all", e))?;
}
Ok(written)
}
+43
View File
@@ -0,0 +1,43 @@
use std::borrow::Cow;
use ironrdp_connector::{ConnectorError, ConnectorErrorExt, ConnectorResult};
use ironrdp_pdu::{rdp, PduParsing};
pub fn encode_send_data_indication<T>(
initiator_id: u16,
channel_id: u16,
user_msg: &T,
buf: &mut Vec<u8>,
) -> ConnectorResult<usize>
where
T: PduParsing,
ConnectorError: From<T::Error>,
{
let user_data_len = user_msg.buffer_length();
let mut user_data = Vec::with_capacity(user_data_len);
user_msg.to_buffer(&mut user_data)?;
let pdu = ironrdp_pdu::mcs::SendDataIndication {
initiator_id,
channel_id,
user_data: Cow::Owned(user_data),
};
let written = ironrdp_pdu::encode_buf(&pdu, buf).map_err(ConnectorError::pdu)?;
Ok(written)
}
pub fn wrap_share_data(pdu: rdp::headers::ShareDataPdu, io_channel_id: u16) -> rdp::headers::ShareControlHeader {
rdp::headers::ShareControlHeader {
share_id: 0,
pdu_source: io_channel_id,
share_control_pdu: rdp::headers::ShareControlPdu::Data(rdp::headers::ShareDataHeader {
share_data_pdu: pdu,
stream_priority: rdp::headers::StreamPriority::Undefined,
compression_flags: rdp::headers::CompressionFlags::empty(),
compression_type: rdp::client_info::CompressionType::K8,
}),
}
}
+19 -1
View File
@@ -86,7 +86,25 @@ pub fn rle_decompress_bitmap(input: BitmapInput) {
let _ = ironrdp_graphics::rle::decompress_8_bpp(input.src, &mut out, input.width, input.height);
}
pub fn rdp6_decode_bitmap_stream_to_rgb24(input: BitmapInput) {
pub fn rdp6_encode_bitmap_stream(input: &BitmapInput) {
use ironrdp_graphics::rdp6::{BitmapStreamEncoder, RgbAChannels, RgbChannels};
let mut out = vec![0; input.src.len() * 2];
let _ = BitmapStreamEncoder::new(input.width.into(), input.height.into()).encode_bitmap::<RgbChannels>(
input.src,
out.as_mut_slice(),
false,
);
let _ = BitmapStreamEncoder::new(input.width.into(), input.height.into()).encode_bitmap::<RgbAChannels>(
input.src,
out.as_mut_slice(),
true,
);
}
pub fn rdp6_decode_bitmap_stream_to_rgb24(input: &BitmapInput) {
use ironrdp_graphics::rdp6::BitmapStreamDecoder;
let mut out = Vec::new();
@@ -88,6 +88,24 @@ pub enum PixelFormat {
RgbX32 = 537_069_704,
}
impl TryFrom<u32> for PixelFormat {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
536_971_400 => Ok(PixelFormat::ARgb32),
536_938_632 => Ok(PixelFormat::XRgb32),
537_036_936 => Ok(PixelFormat::ABgr32),
537_004_168 => Ok(PixelFormat::XBgr32),
537_168_008 => Ok(PixelFormat::BgrA32),
537_135_240 => Ok(PixelFormat::BgrX32),
537_102_472 => Ok(PixelFormat::RgbA32),
537_069_704 => Ok(PixelFormat::RgbX32),
_ => Err(()),
}
}
}
impl PixelFormat {
pub const fn bytes_per_pixel(self) -> u8 {
match self {
@@ -1,16 +1,16 @@
use ironrdp_pdu::bitmap::rdp6::{BitmapStream as BitmapStreamPdu, ColorPlanes};
use ironrdp_pdu::bitmap::rdp6::{BitmapStream as BitmapStreamPdu, ColorPlaneDefinition};
use ironrdp_pdu::{decode, PduError};
use thiserror::Error;
use crate::color_conversion::Rgb;
use crate::rdp6::rle::{decompress_8bpp_plane, RleError};
use crate::rdp6::rle::{decompress_8bpp_plane, RleDecodeError};
#[derive(Debug, Error)]
pub enum BitmapDecodeError {
#[error("Failed to decode RDP6 bitmap stream PDU: {0}")]
Pdu(#[from] PduError),
#[error("Failed to perform RLE decompression of RDP6 bitmap stream: {0}")]
Rle(#[from] RleError),
Rle(#[from] RleDecodeError),
#[error("Color plane data size provided in PDU is not sufficient to reconstruct the bitmap")]
InvalidUncompressedDataSize,
}
@@ -78,7 +78,7 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
}
fn decompress_planes(&'a self, aux_buffer: &'a mut Vec<u8>) -> Result<&'a [u8], BitmapDecodeError> {
let planes = if self.bitmap.enable_rle_compression {
let planes = if self.bitmap.header.enable_rle_compression {
// We don't care for the previous content, just resize it to fit the data
aux_buffer.resize(self.uncompressed_planes_size, 0);
let uncompressed_planes_buffer = &mut aux_buffer[..self.uncompressed_planes_size];
@@ -87,7 +87,7 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
let mut src_offset = 0;
// Decompress Alpha plane
if self.bitmap.use_alpha {
if self.bitmap.header.use_alpha {
// Decompress alpha alpha, but discard it (always 0xFF)
src_offset += decompress_8bpp_plane(
&compressed[src_offset..],
@@ -124,7 +124,11 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
&uncompressed_planes_buffer[..self.uncompressed_planes_size]
} else {
// Discard alpha plane
let color_planes_offset = if self.bitmap.use_alpha { self.full_plane_size } else { 0 };
let color_planes_offset = if self.bitmap.header.use_alpha {
self.full_plane_size
} else {
0
};
let expected_data_size = color_planes_offset + self.uncompressed_planes_size;
@@ -194,12 +198,12 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
// Reserve enough space for decoded RGB channels data
dst.reserve(self.image_height * self.image_width * 3);
match self.bitmap.color_planes {
ColorPlanes::Argb { .. } => {
match self.bitmap.header.color_plane_definition {
ColorPlaneDefinition::Argb => {
let color_planes = self.decompress_planes(aux_buffer)?;
self.write_argb_planes_to_rgb24(color_planes, dst);
}
ColorPlanes::AYCoCg {
ColorPlaneDefinition::AYCoCg {
color_loss_level,
use_chroma_subsampling,
..
@@ -207,7 +211,7 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
let params: AYCoCgParams = AYCoCgParams {
color_loss_level,
chroma_subsampling: use_chroma_subsampling,
alpha: self.bitmap.use_alpha,
alpha: self.bitmap.header.use_alpha,
};
let color_planes = self.decompress_planes(aux_buffer)?;
self.write_aycocg_planes_to_rgb24(params, color_planes, dst);
@@ -261,95 +265,3 @@ impl BitmapStreamDecoder {
decoder.decode(dst, &mut self.planes_buffer)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_decoded_image(pdu: &[u8], expected_bmp: &[u8], width: usize, height: usize) {
let expected_bmp = bmp::from_reader(&mut std::io::Cursor::new(expected_bmp)).unwrap();
let mut expected_buffer = vec![0; width * height * 3];
for (idx, (x, y)) in expected_bmp.coordinates().enumerate() {
let pixel = expected_bmp.get_pixel(x, y);
let offset = idx * 3;
expected_buffer[offset] = pixel.r;
expected_buffer[offset + 1] = pixel.g;
expected_buffer[offset + 2] = pixel.b;
}
let mut actual = Vec::new();
BitmapStreamDecoder::default()
.decode_bitmap_stream_to_rgb24(pdu, &mut actual, width, height)
.unwrap();
assert_eq!(actual.as_slice(), expected_buffer.as_slice());
}
#[test]
fn decode_32x64_rgb_raw() {
// RGB (No alpha), no RLE
assert_decoded_image(
include_bytes!("test_assets/32x64_rgb_raw.bin"),
include_bytes!("test_assets/32x64_rgb_raw.bmp"),
32,
64,
);
}
#[test]
fn decode_64x24_argb_rle() {
// ARGB (With alpha), RLE
assert_decoded_image(
include_bytes!("test_assets/64x24_argb_rle.bin"),
include_bytes!("test_assets/64x24_argb_rle.bmp"),
64,
24,
);
}
#[test]
fn decode_64x64_aycocg_rle() {
// AYCoCg (With alpha), RLE, no chroma subsampling
assert_decoded_image(
include_bytes!("test_assets/64x64_aycocg_rle.bin"),
include_bytes!("test_assets/64x64_aycocg_rle.bmp"),
64,
64,
);
}
#[test]
fn decode_64x64_ycocg_rle_ss() {
// AYCoCg (No alpha), RLE, with chroma subsampling
assert_decoded_image(
include_bytes!("test_assets/64x64_ycocg_rle_ss.bin"),
include_bytes!("test_assets/64x64_ycocg_rle_ss.bmp"),
64,
64,
);
}
#[test]
fn decode_64x35_ycocg_rle_ss() {
// AYCoCg (No alpha), RLE, with chroma subsampling + odd resolution
assert_decoded_image(
include_bytes!("test_assets/64x35_ycocg_rle_ss.bin"),
include_bytes!("test_assets/64x35_ycocg_rle_ss.bmp"),
64,
35,
);
}
#[test]
fn decode_64x64_ycocg_raw_ss() {
// AYCoCg (No alpha), no RLE, with chroma subsampling
assert_decoded_image(
include_bytes!("test_assets/64x64_ycocg_raw_ss.bin"),
include_bytes!("test_assets/64x64_ycocg_raw_ss.bmp"),
64,
64,
);
}
}
@@ -0,0 +1,283 @@
use ironrdp_pdu::{
bitmap::rdp6::{BitmapStreamHeader, ColorPlaneDefinition},
cursor::WriteCursor,
PduError,
};
use thiserror::Error;
use crate::rdp6::rle::{compress_8bpp_plane, RleEncodeError};
#[derive(Debug, Error)]
pub enum BitmapEncodeError {
#[error("Failed to rle compress")]
Rle(RleEncodeError),
#[error("Failed to encode pdu")]
Pdu(PduError),
}
pub trait ColorChannels {
const STRIDE: usize;
const R: usize;
const G: usize;
const B: usize;
}
pub trait AlphaChannel {
const A: usize;
}
pub trait PixelFormat {
const STRIDE: usize;
fn r(pixel: &[u8]) -> u8;
fn g(pixel: &[u8]) -> u8;
fn b(pixel: &[u8]) -> u8;
}
pub trait PixelAlpha: PixelFormat {
fn a(pixel: &[u8]) -> u8;
}
impl<T> PixelFormat for T
where
T: ColorChannels,
{
const STRIDE: usize = T::STRIDE;
fn r(pixel: &[u8]) -> u8 {
pixel[T::R]
}
fn g(pixel: &[u8]) -> u8 {
pixel[T::G]
}
fn b(pixel: &[u8]) -> u8 {
pixel[T::B]
}
}
impl<T> PixelAlpha for T
where
T: ColorChannels + AlphaChannel,
{
fn a(pixel: &[u8]) -> u8 {
pixel[T::A]
}
}
pub struct RgbChannels;
impl ColorChannels for RgbChannels {
const STRIDE: usize = 3;
const R: usize = 0;
const G: usize = 1;
const B: usize = 2;
}
pub struct ARgbChannels;
impl ColorChannels for ARgbChannels {
const STRIDE: usize = 4;
const R: usize = 1;
const G: usize = 2;
const B: usize = 3;
}
impl AlphaChannel for ARgbChannels {
const A: usize = 0;
}
pub struct RgbAChannels;
impl ColorChannels for RgbAChannels {
const STRIDE: usize = 4;
const R: usize = 0;
const G: usize = 1;
const B: usize = 2;
}
impl AlphaChannel for RgbAChannels {
const A: usize = 3;
}
pub struct ABgrChannels;
impl ColorChannels for ABgrChannels {
const STRIDE: usize = 4;
const R: usize = 3;
const G: usize = 2;
const B: usize = 1;
}
impl AlphaChannel for ABgrChannels {
const A: usize = 0;
}
pub struct BgrAChannels;
impl ColorChannels for BgrAChannels {
const STRIDE: usize = 4;
const R: usize = 2;
const G: usize = 1;
const B: usize = 0;
}
impl AlphaChannel for BgrAChannels {
const A: usize = 3;
}
impl BitmapEncodeError {
fn rle(e: RleEncodeError) -> Self {
Self::Rle(e)
}
}
pub struct BitmapStreamEncoder {
width: usize,
height: usize,
}
impl BitmapStreamEncoder {
pub fn new(width: usize, height: usize) -> Self {
Self { width, height }
}
pub fn encode_channels_stream<R, G, B>(
&mut self,
(r, g, b): (R, G, B),
dst: &mut [u8],
rle: bool,
) -> Result<usize, BitmapEncodeError>
where
R: Iterator<Item = u8>,
G: Iterator<Item = u8>,
B: Iterator<Item = u8>,
{
let mut cursor = WriteCursor::new(dst);
let header = BitmapStreamHeader {
enable_rle_compression: rle,
use_alpha: false,
color_plane_definition: ColorPlaneDefinition::Argb,
};
ironrdp_pdu::encode_cursor(&header, &mut cursor).map_err(BitmapEncodeError::Pdu)?;
match rle {
true => {
compress_8bpp_plane(r, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::Rle)?;
compress_8bpp_plane(g, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::Rle)?;
compress_8bpp_plane(b, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::Rle)?;
}
false => {
let remaining = cursor.remaining().len();
let needed = self.width * self.height * 3 + 1;
if needed > remaining {
return Err(BitmapEncodeError::Pdu(
<PduError as ironrdp_pdu::PduErrorExt>::not_enough_bytes("BitmapStreamData", remaining, needed),
));
}
for byte in r.chain(g).chain(b) {
cursor.write_u8(byte);
}
cursor.write_u8(0u8);
}
};
Ok(cursor.pos())
}
pub fn encode_pixels_stream<'a, I, F>(
&mut self,
data: I,
dst: &mut [u8],
rle: bool,
) -> Result<usize, BitmapEncodeError>
where
F: PixelFormat,
I: Iterator<Item = &'a [u8]> + Clone,
{
let r = data.clone().map(F::r);
let g = data.clone().map(F::g);
let b = data.map(F::b);
self.encode_channels_stream((r, g, b), dst, rle)
}
pub fn encode_bitmap<F>(&mut self, src: &[u8], dst: &mut [u8], rle: bool) -> Result<usize, BitmapEncodeError>
where
F: PixelFormat,
{
let r = src.chunks_exact(F::STRIDE).map(F::r);
let g = src.chunks_exact(F::STRIDE).map(F::g);
let b = src.chunks_exact(F::STRIDE).map(F::b);
self.encode_channels_stream((r, g, b), dst, rle)
}
}
impl BitmapStreamEncoder {
pub fn encode_channels_stream_alpha<R, G, B, A>(
&mut self,
(r, g, b, a): (R, G, B, A),
dst: &mut [u8],
rle: bool,
) -> Result<usize, BitmapEncodeError>
where
R: Iterator<Item = u8>,
G: Iterator<Item = u8>,
B: Iterator<Item = u8>,
A: Iterator<Item = u8>,
{
let mut cursor = WriteCursor::new(dst);
let header = BitmapStreamHeader {
enable_rle_compression: rle,
use_alpha: false,
color_plane_definition: ColorPlaneDefinition::Argb,
};
ironrdp_pdu::encode_cursor(&header, &mut cursor).map_err(BitmapEncodeError::Pdu)?;
match rle {
true => {
compress_8bpp_plane(a, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::rle)?;
compress_8bpp_plane(r, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::rle)?;
compress_8bpp_plane(g, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::rle)?;
compress_8bpp_plane(b, &mut cursor, self.width, self.height).map_err(BitmapEncodeError::rle)?;
}
false => {
let remaining = cursor.remaining().len();
let needed = self.width * self.height * 4 + 1;
if needed > remaining {
return Err(BitmapEncodeError::Pdu(
<PduError as ironrdp_pdu::PduErrorExt>::not_enough_bytes("BitmapStreamData", remaining, needed),
));
}
for byte in a.chain(r).chain(g).chain(b) {
cursor.write_u8(byte);
}
cursor.write_u8(0u8);
}
};
Ok(cursor.pos())
}
pub fn encode_bitmap_alpha<F>(&mut self, src: &[u8], dst: &mut [u8], rle: bool) -> Result<usize, BitmapEncodeError>
where
F: PixelFormat + PixelAlpha,
{
let r = src.chunks_exact(F::STRIDE).map(F::r);
let g = src.chunks_exact(F::STRIDE).map(F::g);
let b = src.chunks_exact(F::STRIDE).map(F::b);
let a = src.chunks_exact(F::STRIDE).map(F::a);
self.encode_channels_stream_alpha((r, g, b, a), dst, rle)
}
}
@@ -0,0 +1,179 @@
mod decoder;
mod encoder;
pub use decoder::*;
pub use encoder::*;
#[cfg(test)]
mod tests {
use super::*;
fn buffer_from_bmp(bmp_image: &[u8], width: usize, height: usize) -> Vec<u8> {
let expected_bmp = bmp::from_reader(&mut std::io::Cursor::new(bmp_image)).unwrap();
let mut expected_buffer = vec![0; width * height * 3];
for (idx, (x, y)) in expected_bmp.coordinates().enumerate() {
let pixel = expected_bmp.get_pixel(x, y);
let offset = idx * 3;
expected_buffer[offset] = pixel.r;
expected_buffer[offset + 1] = pixel.g;
expected_buffer[offset + 2] = pixel.b;
}
expected_buffer
}
fn assert_decoded_image(pdu: &[u8], expected_bmp: &[u8], width: usize, height: usize) {
let expected_buffer = buffer_from_bmp(expected_bmp, width, height);
let mut actual = Vec::new();
BitmapStreamDecoder::default()
.decode_bitmap_stream_to_rgb24(pdu, &mut actual, width, height)
.unwrap();
assert_eq!(actual.as_slice(), expected_buffer.as_slice());
}
#[test]
fn decode_32x64_rgb_raw() {
// RGB (No alpha), no RLE
assert_decoded_image(
include_bytes!("../test_assets/32x64_rgb_raw.bin"),
include_bytes!("../test_assets/32x64_rgb_raw.bmp"),
32,
64,
);
}
#[test]
fn decode_64x24_argb_rle() {
// ARGB (With alpha), RLE
assert_decoded_image(
include_bytes!("../test_assets/64x24_argb_rle.bin"),
include_bytes!("../test_assets/64x24_argb_rle.bmp"),
64,
24,
);
}
#[test]
fn decode_64x64_aycocg_rle() {
// AYCoCg (With alpha), RLE, no chroma subsampling
assert_decoded_image(
include_bytes!("../test_assets/64x64_aycocg_rle.bin"),
include_bytes!("../test_assets/64x64_aycocg_rle.bmp"),
64,
64,
);
}
#[test]
fn decode_64x64_ycocg_rle_ss() {
// AYCoCg (No alpha), RLE, with chroma subsampling
assert_decoded_image(
include_bytes!("../test_assets/64x64_ycocg_rle_ss.bin"),
include_bytes!("../test_assets/64x64_ycocg_rle_ss.bmp"),
64,
64,
);
}
#[test]
fn decode_64x35_ycocg_rle_ss() {
// AYCoCg (No alpha), RLE, with chroma subsampling + odd resolution
assert_decoded_image(
include_bytes!("../test_assets/64x35_ycocg_rle_ss.bin"),
include_bytes!("../test_assets/64x35_ycocg_rle_ss.bmp"),
64,
35,
);
}
#[test]
fn decode_64x64_ycocg_raw_ss() {
// AYCoCg (No alpha), no RLE, with chroma subsampling
assert_decoded_image(
include_bytes!("../test_assets/64x64_ycocg_raw_ss.bin"),
include_bytes!("../test_assets/64x64_ycocg_raw_ss.bmp"),
64,
64,
);
}
fn assert_encoded_image(expected_pdu: &[u8], bmp: &[u8], width: usize, height: usize, rle: bool) {
let image = buffer_from_bmp(bmp, width, height);
let mut pdu = vec![0; width * height * 4 + 2];
let written = BitmapStreamEncoder::new(width, height)
.encode_bitmap::<RgbChannels>(&image, &mut pdu, rle)
.unwrap();
// last byte is padding when !rle
assert_eq!(&pdu[0..written - 1], &expected_pdu[0..written - 1]);
}
fn encode_decode_test(bmp: &[u8], width: usize, height: usize, rle: bool) {
let image = buffer_from_bmp(bmp, width, height);
let mut pdu = vec![0; width * height * 4 + 2];
let written = BitmapStreamEncoder::new(width, height)
.encode_bitmap::<RgbChannels>(&image, &mut pdu, rle)
.unwrap();
let mut actual = Vec::new();
BitmapStreamDecoder::default()
.decode_bitmap_stream_to_rgb24(&pdu[..written], &mut actual, width, height)
.unwrap();
assert_eq!(&image.as_slice(), &actual.as_slice());
}
#[test]
fn encode_32x64_rgb_raw() {
// RGB (No alpha), no RLE
assert_encoded_image(
include_bytes!("../test_assets/32x64_rgb_raw.bin"),
include_bytes!("../test_assets/32x64_rgb_raw.bmp"),
32,
64,
false,
);
}
#[test]
fn encode_decode_32x64_rgb_raw() {
// RGB (No alpha), no RLE
encode_decode_test(include_bytes!("../test_assets/32x64_rgb_raw.bmp"), 32, 64, false);
}
#[test]
fn encode_decode_32x64_rgb_rle() {
// RGB (No alpha), with RLE
encode_decode_test(include_bytes!("../test_assets/32x64_rgb_raw.bmp"), 32, 64, true);
}
#[test]
fn encode_decode_64x24_rgb_raw() {
// RGB (No alpha), no RLE
encode_decode_test(include_bytes!("../test_assets/64x24_argb_rle.bmp"), 32, 64, false);
}
#[test]
fn encode_decode_64x24_rgb_rle() {
// RGB (No alpha), with RLE
encode_decode_test(include_bytes!("../test_assets/64x24_argb_rle.bmp"), 32, 64, true);
}
#[test]
fn encode_decode_64x64_rgb_raw() {
// RGB (No alpha), no RLE
encode_decode_test(include_bytes!("../test_assets/64x64_aycocg_rle.bmp"), 64, 64, false);
}
#[test]
fn encode_decode_64x64_rgb_rle() {
// RGB (No alpha), with RLE
encode_decode_test(include_bytes!("../test_assets/64x64_aycocg_rle.bmp"), 64, 64, true);
}
}
+2 -2
View File
@@ -3,5 +3,5 @@
pub(crate) mod bitmap_stream;
pub(crate) mod rle;
pub use bitmap_stream::{BitmapDecodeError, BitmapStreamDecoder};
pub use rle::RleError;
pub use bitmap_stream::*;
pub use rle::{RleDecodeError, RleEncodeError};
+373 -26
View File
@@ -1,6 +1,7 @@
use std::io::{Read, Write};
use byteorder::ReadBytesExt;
use ironrdp_pdu::cursor::WriteCursor;
use thiserror::Error;
/// Maximum possible segment size is 47 (run_length = 2, raw_bytes_count = 15), which is treated as
@@ -8,7 +9,7 @@ use thiserror::Error;
const MAX_DECODED_SEGMENT_SIZE: usize = 47;
#[derive(Debug, Error)]
pub enum RleError {
pub enum RleDecodeError {
#[error("Failed to read RLE-compressed data: {0}")]
ReadCompressedData(#[source] std::io::Error),
@@ -22,6 +23,15 @@ pub enum RleError {
SegmentDoNotFitScanline,
}
#[derive(Debug, Error)]
pub enum RleEncodeError {
#[error("Not enough data to compress")]
NotEnoughBytes,
#[error("Destination buffer is too small")]
BufferTooSmall,
}
/// RLE-encoded color plane decoder implementation for RDP6 bitmap stream
#[derive(Debug)]
struct RlePlaneDecoder {
@@ -47,11 +57,11 @@ impl RlePlaneDecoder {
}
}
fn decompress_next_segment(&mut self, mut src: &[u8]) -> Result<usize, RleError> {
let control_byte = src.read_u8().map_err(RleError::ReadCompressedData)?;
fn decompress_next_segment(&mut self, mut src: &[u8]) -> Result<usize, RleDecodeError> {
let control_byte = src.read_u8().map_err(RleDecodeError::ReadCompressedData)?;
if control_byte == 0 {
return Err(RleError::InvalidSegmentHeader);
return Err(RleDecodeError::InvalidSegmentHeader);
}
let rle_bytes_field = control_byte & 0x0F;
@@ -66,7 +76,7 @@ impl RlePlaneDecoder {
self.decoded_data_len = raw_bytes_count + run_length;
src.read_exact(&mut self.decoded_data[..raw_bytes_count])
.map_err(RleError::ReadCompressedData)?;
.map_err(RleDecodeError::ReadCompressedData)?;
if raw_bytes_count > 0 {
// save last decoded byte for the next segments decoding
@@ -79,7 +89,7 @@ impl RlePlaneDecoder {
}
/// Decodes single RLE-encoded scanline, without performing delta transformation
fn decode_scanline(&mut self, src: &[u8], mut dst: &mut [u8]) -> Result<usize, RleError> {
fn decode_scanline(&mut self, src: &[u8], mut dst: &mut [u8]) -> Result<usize, RleDecodeError> {
let mut decoded_columns = 0;
let mut read_bytes = 0;
@@ -89,11 +99,11 @@ impl RlePlaneDecoder {
read_bytes += self.decompress_next_segment(&src[read_bytes..])?;
if decoded_columns + self.decoded_data_len > self.width {
return Err(RleError::SegmentDoNotFitScanline);
return Err(RleDecodeError::SegmentDoNotFitScanline);
}
dst.write_all(&self.decoded_data[..self.decoded_data_len])
.map_err(RleError::WriteDecompressedData)?;
.map_err(RleDecodeError::WriteDecompressedData)?;
decoded_columns += self.decoded_data_len;
}
@@ -122,7 +132,7 @@ impl RlePlaneDecoder {
});
}
pub fn decode(mut self, src: &[u8], dst: &mut [u8]) -> Result<usize, RleError> {
pub fn decode(mut self, src: &[u8], dst: &mut [u8]) -> Result<usize, RleDecodeError> {
let mut read_bytes = 0;
read_bytes += self.decode_scanline(src, dst)?;
@@ -147,18 +157,214 @@ impl RlePlaneDecoder {
/// Size of data written to dst buffer is exactly equal to `width * height`.
///
/// Returns number of bytes consumed from src buffer.
pub fn decompress_8bpp_plane(
src: &[u8],
dst: &mut [u8],
width: impl Into<usize>,
height: impl Into<usize>,
) -> Result<usize, RleError> {
let width = width.into();
let height = height.into();
pub fn decompress_8bpp_plane(src: &[u8], dst: &mut [u8], width: usize, height: usize) -> Result<usize, RleDecodeError> {
RlePlaneDecoder::new(width, height).decode(src, dst)
}
struct RleEncoderScanlineIterator<I> {
inner: std::iter::Enumerate<I>,
width: usize,
prev_scanline: Vec<u8>,
}
impl<I: Iterator> RleEncoderScanlineIterator<I> {
fn new(width: usize, inner: I) -> Self {
Self {
width,
inner: inner.enumerate(),
prev_scanline: vec![0; width],
}
}
fn delta_value(&self, prev: u8, next: u8) -> u8 {
let mut result = (next as i16 - prev as i16) as u8;
// bit magic from 3.1.9.2.1 of [MS-RDPEGDI].
if result < 128 {
result <<= 1;
} else {
result = (255u8.wrapping_sub(result) << 1).wrapping_add(1);
}
result
}
}
impl<I: Iterator<Item = u8>> Iterator for RleEncoderScanlineIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
let Some((idx, mut next)) = self.inner.next() else {
return None;
};
let prev = std::mem::replace(&mut self.prev_scanline[idx % self.width], next);
if idx >= self.width {
next = self.delta_value(prev, next);
}
Some(next)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Debug)]
struct RlePlaneEncoder {
width: usize,
height: usize,
}
macro_rules! ensure_size {
(dst: $buf:ident, size: $expected:expr) => {{
let available = $buf.len();
let needed = $expected;
if !(available >= needed) {
return None;
}
}};
}
impl RlePlaneEncoder {
pub fn new(width: usize, height: usize) -> Self {
Self { width, height }
}
pub fn encode(
&self,
mut src: impl Iterator<Item = u8>,
dst: &mut WriteCursor<'_>,
) -> Result<usize, RleEncodeError> {
let mut written = 0;
for _ in 0..self.height {
written += self.encode_scanline((&mut src).take(self.width), dst)?;
}
Ok(written)
}
fn encode_scanline(
&self,
mut src: impl Iterator<Item = u8>,
dst: &mut WriteCursor<'_>,
) -> Result<usize, RleEncodeError> {
let mut written = 0;
let first = src.next().ok_or(RleEncodeError::NotEnoughBytes)?;
let mut raw = vec![first];
let mut seq = (first, 0);
for byte in src {
let (last, count) = seq;
seq = if byte == last {
(byte, count + 1)
} else {
match count {
3.. => {
written += self
.encode_segment(&raw, count, dst)
.ok_or(RleEncodeError::BufferTooSmall)?;
raw.clear();
}
2 => raw.extend_from_slice(&[last, last]),
1 => raw.push(last),
_ => {}
}
raw.push(byte);
(byte, 0)
}
}
let (last, mut count) = seq;
if count < 3 {
raw.extend(vec![last; count].into_iter());
count = 0;
}
written += self
.encode_segment(&raw, count, dst)
.ok_or(RleEncodeError::BufferTooSmall)?;
Ok(written)
}
fn encode_segment(&self, mut raw: &[u8], run: usize, dst: &mut WriteCursor<'_>) -> Option<usize> {
let mut extra_bytes = 0;
while raw.len() > 15 {
extra_bytes += self.encode_segment(&raw[0..15], 0, dst)?;
raw = &raw[15..];
}
let control = ((raw.len() as u8) << 4) + std::cmp::min(run, 15) as u8;
ensure_size!(dst: dst, size: raw.len() + 1);
dst.write_u8(control);
dst.write_slice(raw);
if run > 15 {
let last = raw.last().unwrap();
extra_bytes += self.encode_long_sequence(run - 15, *last, dst)?;
}
Some(1 + raw.len() + extra_bytes)
}
fn encode_long_sequence(&self, mut run: usize, last: u8, dst: &mut WriteCursor<'_>) -> Option<usize> {
let mut written = 0;
while run >= 16 {
ensure_size!(dst: dst, size: 1);
let current = std::cmp::min(run, MAX_DECODED_SEGMENT_SIZE) as u8;
let c_raw_bytes = std::cmp::min(current / 16, 2);
let n_run_length = current - c_raw_bytes * 16;
let control = (n_run_length << 4) + c_raw_bytes;
dst.write_u8(control);
written += 1;
run -= current as usize;
}
if run > 0 {
match run {
short @ 1..=3 => {
written += self.encode_segment(&vec![last; short], 0, dst)?;
}
long => {
written += self.encode_segment(&[last], long - 1, dst)?;
}
}
}
Some(written)
}
}
/// Performs compression of 8bpp color plane pixel stream into a buffer.
/// Pixel iterator must have at least width * height items.
/// Destination slice must have enough space for the compressed data.
///
/// Returns number of bytes written to the dst buffer.
pub fn compress_8bpp_plane(
src: impl Iterator<Item = u8>,
dst: &mut WriteCursor<'_>,
width: usize,
height: usize,
) -> Result<usize, RleEncodeError> {
let iter = RleEncoderScanlineIterator::new(width, src);
RlePlaneEncoder::new(width, height).encode(iter, dst)
}
#[cfg(test)]
mod tests {
use expect_test::expect;
@@ -166,20 +372,70 @@ mod tests {
use super::*;
/// Performs decompression of 8bpp color plane into vector. Vector will be resized to fit decompressed data.
pub fn decompress(
src: &[u8],
dst: &mut Vec<u8>,
width: impl Into<usize>,
height: impl Into<usize>,
) -> Result<usize, RleError> {
let width = width.into();
let height = height.into();
pub fn decompress(src: &[u8], dst: &mut Vec<u8>, width: usize, height: usize) -> Result<usize, RleDecodeError> {
// Ensure dest buffer have enough space for decompressed data
dst.resize(width * height, 0);
decompress_8bpp_plane(src, dst.as_mut_slice(), width, height)
}
pub fn compress(src: &[u8], dst: &mut Vec<u8>, width: usize, height: usize) -> Result<usize, RleEncodeError> {
compress_8bpp_plane(src.iter().copied(), &mut WriteCursor::new(dst), width, height)
}
#[test]
fn simple_encode() {
// Example AAAABBCCCCCD from 3.1.9.2 of [MS-RDPEGDI].
let src = [65, 65, 65, 65, 66, 66, 67, 67, 67, 67, 67, 68];
let width = src.len();
let height = 1usize;
let expected = &[0x13, 65, 0x34, 66, 66, 67, 0x10, 68];
let mut compressed = vec![0; 255];
let len = compress(&src, &mut compressed, width, height).unwrap();
assert_eq!(&compressed[..len], expected);
}
#[test]
fn long_sequence_encode() {
// Example from 3.1.9.2.2 of [MS-RDPEGDI].
let src = [0x41u8; 100];
let width = 100usize;
let height = 1usize;
let expected = &[0x1F, 0x41, 0xF2, 0x52];
let mut compressed = vec![0; 255];
let len = compress(&src, &mut compressed, width, height).unwrap();
assert_eq!(&compressed[..len], expected);
}
#[test]
fn multiline_encode() {
// Example from 3.1.9.2.1 of [MS-RDPEGDI].
let src = [
255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
];
let width = 6usize;
let height = 3usize;
let expected = &[
0x13, 0xFF, 0x20, 0xFE, 0xFD, 0x60, 0x01, 0x7D, 0xF5, 0xC2, 0x9A, 0x38, 0x60, 0x01, 0x67, 0x8B, 0xA3, 0x78,
0xAF,
];
let mut compressed = vec![0; 255];
let len = compress(&src, &mut compressed, width, height).unwrap();
assert_eq!(&compressed[..len], expected);
}
#[test]
fn long_sequence_decode() {
// Example from 3.1.9.2.2 of [MS-RDPEGDI].
@@ -215,6 +471,61 @@ mod tests {
assert_eq!(actual, expected);
}
#[test]
fn long_sequence_encode_decode() {
// Example from 3.1.9.2.2 of [MS-RDPEGDI].
let src = [0x41u8; 100];
let width = 100usize;
let height = 1usize;
let mut compressed = vec![0; 255];
let len = compress(&src, &mut compressed, width, height).unwrap();
let mut actual = Vec::new();
decompress(&compressed[..len], &mut actual, width, height).unwrap();
assert_eq!(actual.as_slice(), src.as_slice());
}
#[test]
fn complex_encode_decode() {
let src = [
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 19, 19, 18, 18, 18,
18, 18, 18, 18, 18,
];
let width = src.len();
let height = 1usize;
let mut compressed = vec![0; 255];
let len = compress(&src, &mut compressed, width, height).unwrap();
let mut actual = Vec::new();
decompress(&compressed[..len], &mut actual, width, height).unwrap();
assert_eq!(actual.as_slice(), src.as_slice());
}
#[test]
fn multiline_encode_decode() {
// Example from 3.1.9.2.3 of [MS-RDPEGDI].
let src = [
255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
];
let width = 6usize;
let height = 3usize;
let mut compressed = vec![0; 255];
let len = compress(&src, &mut compressed, width, height).unwrap();
let mut actual = Vec::new();
decompress(&compressed[..len], &mut actual, width, height).unwrap();
assert_eq!(actual.as_slice(), src.as_slice());
}
#[test]
fn each_scanline_resets_last_decoded_byte() {
let src = [0x17, 0xFF, 0x04, 0x40, 0x01, 0x02, 0x03, 0x04];
@@ -309,6 +620,42 @@ mod tests {
.assert_debug_eq(&decompress(&src, &mut actual, width, height));
}
#[test]
fn buffer_too_small_encode() {
let src = [
255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
];
let width = 6usize;
let height = 3usize;
let mut compressed = vec![0; 4];
expect![[r#"
Err(
BufferTooSmall,
)
"#]]
.assert_debug_eq(&compress(&src, &mut compressed, width, height));
}
#[test]
fn not_enough_bytes_to_encode() {
let src = [255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253];
let width = 8usize;
let height = 3usize;
let mut compressed = vec![0; 255];
expect![[r#"
Err(
NotEnoughBytes,
)
"#]]
.assert_debug_eq(&compress(&src, &mut compressed, width, height));
}
#[test]
fn too_small_dest_buffer_handled() {
let src = [0x17, 0xFF, 0x04, 0x40, 0x01, 0x02, 0x03, 0x04];
+12 -2
View File
@@ -24,6 +24,17 @@ impl BitmapUpdateData<'_> {
const FIXED_PART_SIZE: usize = core::mem::size_of::<u16>() * 2;
}
impl BitmapUpdateData<'_> {
pub fn encode_header(rectangles: u16, dst: &mut crate::cursor::WriteCursor<'_>) -> PduResult<()> {
ensure_size!(in: dst, size: 2);
dst.write_u16(BitmapFlags::BITMAP_UPDATE_TYPE.bits());
dst.write_u16(rectangles);
Ok(())
}
}
impl<'en> PduEncode for BitmapUpdateData<'en> {
fn encode(&self, dst: &mut crate::cursor::WriteCursor<'_>) -> PduResult<()> {
ensure_size!(in: dst, size: self.size());
@@ -32,8 +43,7 @@ impl<'en> PduEncode for BitmapUpdateData<'en> {
return Err(invalid_message_err!("numberRectangles", "rectangle count is too big"));
}
dst.write_u16(BitmapFlags::BITMAP_UPDATE_TYPE.bits());
dst.write_u16(self.rectangles.len() as u16);
Self::encode_header(self.rectangles.len() as u16, dst)?;
for bitmap_data in self.rectangles.iter() {
bitmap_data.encode(dst)?;
+135 -102
View File
@@ -3,23 +3,96 @@ use crate::{PduDecode, PduEncode, PduResult, ReadCursor, WriteCursor};
const NON_RLE_PADDING_SIZE: usize = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorPlanes<'a> {
Argb {
data: &'a [u8],
},
pub enum ColorPlaneDefinition {
Argb,
AYCoCg {
color_loss_level: u8,
use_chroma_subsampling: bool,
data: &'a [u8],
},
}
/// Represents `RDP6_BITMAP_STREAM` structure described in [MS-RDPEGDI] 2.2.2.5.1
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BitmapStream<'a> {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitmapStreamHeader {
pub enable_rle_compression: bool,
pub use_alpha: bool,
pub color_planes: ColorPlanes<'a>,
pub color_plane_definition: ColorPlaneDefinition,
}
impl BitmapStreamHeader {
pub const NAME: &'static str = "Rdp6BitmapStreamHeader";
const FIXED_PART_SIZE: usize = 1;
}
impl PduDecode<'_> for BitmapStreamHeader {
fn decode(src: &mut ReadCursor<'_>) -> PduResult<Self> {
ensure_fixed_part_size!(in: src);
let header = src.read_u8();
let color_loss_level = header & 0x07;
let use_chroma_subsampling = (header & 0x08) != 0;
let enable_rle_compression = (header & 0x10) != 0;
let use_alpha = (header & 0x20) == 0;
let color_plane_definition = match color_loss_level {
0 => ColorPlaneDefinition::Argb,
color_loss_level => ColorPlaneDefinition::AYCoCg {
color_loss_level,
use_chroma_subsampling,
},
};
Ok(Self {
enable_rle_compression,
use_alpha,
color_plane_definition,
})
}
}
impl PduEncode for BitmapStreamHeader {
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
ensure_size!(in: dst, size: self.size());
let mut header = ((self.enable_rle_compression as u8) << 4) | ((!self.use_alpha as u8) << 5);
match self.color_plane_definition {
ColorPlaneDefinition::Argb { .. } => {
// ARGB color planes keep cll and cs flags set to 0
}
ColorPlaneDefinition::AYCoCg {
color_loss_level,
use_chroma_subsampling,
..
} => {
// Add cll and cs flags to header
header |= (color_loss_level & 0x07) | ((use_chroma_subsampling as u8) << 3);
}
}
dst.write_u8(header);
Ok(())
}
fn name(&self) -> &'static str {
Self::NAME
}
fn size(&self) -> usize {
Self::FIXED_PART_SIZE
+ if self.enable_rle_compression {
0
} else {
NON_RLE_PADDING_SIZE
}
}
}
/// Represents `RDP6_BITMAP_STREAM` structure described in [MS-RDPEGDI] 2.2.2.5.1
#[derive(Debug, Clone)]
pub struct BitmapStream<'a> {
pub header: BitmapStreamHeader,
pub color_planes: &'a [u8],
}
impl<'a> BitmapStream<'a> {
@@ -27,16 +100,13 @@ impl<'a> BitmapStream<'a> {
const FIXED_PART_SIZE: usize = 1;
pub fn color_panes_data(&self) -> &'a [u8] {
match self.color_planes {
ColorPlanes::Argb { data } => data,
ColorPlanes::AYCoCg { data, .. } => data,
}
self.color_planes
}
pub fn has_subsampled_chroma(&self) -> bool {
match self.color_planes {
ColorPlanes::Argb { .. } => false,
ColorPlanes::AYCoCg {
match self.header.color_plane_definition {
ColorPlaneDefinition::Argb { .. } => false,
ColorPlaneDefinition::AYCoCg {
use_chroma_subsampling, ..
} => use_chroma_subsampling,
}
@@ -46,14 +116,9 @@ impl<'a> BitmapStream<'a> {
impl<'a> PduDecode<'a> for BitmapStream<'a> {
fn decode(src: &mut ReadCursor<'a>) -> PduResult<Self> {
ensure_fixed_part_size!(in: src);
let header = src.read_u8();
let header = crate::decode_cursor::<BitmapStreamHeader>(src)?;
let color_loss_level = header & 0x07;
let use_chroma_subsampling = (header & 0x08) != 0;
let enable_rle_compression = (header & 0x10) != 0;
let use_alpha = (header & 0x20) == 0;
let color_planes_size = if !enable_rle_compression {
let color_planes_size = if !header.enable_rle_compression {
// Cut padding field if RLE flags is set to 0
if src.is_empty() {
return Err(invalid_message_err!(
@@ -66,55 +131,21 @@ impl<'a> PduDecode<'a> for BitmapStream<'a> {
src.len()
};
let color_planes_data = src.peek_slice(color_planes_size);
let color_planes = src.peek_slice(color_planes_size);
let color_planes = match color_loss_level {
0 => {
// ARGB color planes
ColorPlanes::Argb {
data: color_planes_data,
}
}
color_loss_level => ColorPlanes::AYCoCg {
color_loss_level,
use_chroma_subsampling,
data: color_planes_data,
},
};
Ok(Self {
enable_rle_compression,
use_alpha,
color_planes,
})
Ok(Self { header, color_planes })
}
}
impl<'a> PduEncode for BitmapStream<'a> {
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
let mut header = ((self.enable_rle_compression as u8) << 4) | ((!self.use_alpha as u8) << 5);
match self.color_planes {
ColorPlanes::Argb { .. } => {
// ARGB color planes keep cll and cs flags set to 0
}
ColorPlanes::AYCoCg {
color_loss_level,
use_chroma_subsampling,
..
} => {
// Add cll and cs flags to header
header |= (color_loss_level & 0x07) | ((use_chroma_subsampling as u8) << 3);
}
}
ensure_size!(in: dst, size: self.size());
dst.write_u8(header);
crate::encode_cursor(&self.header, dst)?;
dst.write_slice(self.color_panes_data());
// Write padding
if !self.enable_rle_compression {
if !self.header.enable_rle_compression {
dst.write_u8(0);
}
@@ -126,11 +157,7 @@ impl<'a> PduEncode for BitmapStream<'a> {
}
fn size(&self) -> usize {
if self.enable_rle_compression {
Self::FIXED_PART_SIZE + self.color_panes_data().len()
} else {
Self::FIXED_PART_SIZE + NON_RLE_PADDING_SIZE + self.color_panes_data().len()
}
self.header.size() + self.color_panes_data().len()
}
}
@@ -162,18 +189,20 @@ mod tests {
&[0x3F, 0x01, 0x02, 0x03, 0x04],
expect![[r#"
BitmapStream {
enable_rle_compression: true,
use_alpha: false,
color_planes: AYCoCg {
color_loss_level: 7,
use_chroma_subsampling: true,
data: [
1,
2,
3,
4,
],
header: BitmapStreamHeader {
enable_rle_compression: true,
use_alpha: false,
color_plane_definition: AYCoCg {
color_loss_level: 7,
use_chroma_subsampling: true,
},
},
color_planes: [
1,
2,
3,
4,
],
}
"#]],
);
@@ -183,16 +212,17 @@ mod tests {
&[0x10, 0x01, 0x02, 0x03, 0x04],
expect![[r#"
BitmapStream {
enable_rle_compression: true,
use_alpha: true,
color_planes: Argb {
data: [
1,
2,
3,
4,
],
header: BitmapStreamHeader {
enable_rle_compression: true,
use_alpha: true,
color_plane_definition: Argb,
},
color_planes: [
1,
2,
3,
4,
],
}
"#]],
);
@@ -202,15 +232,16 @@ mod tests {
&[0x20, 0x01, 0x02, 0x03, 0x00],
expect![[r#"
BitmapStream {
enable_rle_compression: false,
use_alpha: false,
color_planes: Argb {
data: [
1,
2,
3,
],
header: BitmapStreamHeader {
enable_rle_compression: false,
use_alpha: false,
color_plane_definition: Argb,
},
color_planes: [
1,
2,
3,
],
}
"#]],
);
@@ -220,11 +251,12 @@ mod tests {
&[0x10],
expect![[r#"
BitmapStream {
enable_rle_compression: true,
use_alpha: true,
color_planes: Argb {
data: [],
header: BitmapStreamHeader {
enable_rle_compression: true,
use_alpha: true,
color_plane_definition: Argb,
},
color_planes: [],
}
"#]],
);
@@ -234,11 +266,12 @@ mod tests {
&[0x00, 0x00],
expect![[r#"
BitmapStream {
enable_rle_compression: false,
use_alpha: true,
color_planes: Argb {
data: [],
header: BitmapStreamHeader {
enable_rle_compression: false,
use_alpha: true,
color_plane_definition: Argb,
},
color_planes: [],
}
"#]],
);
@@ -27,6 +27,14 @@ impl FastPathHeader {
const NAME: &str = "TS_FP_UPDATE_PDU header";
const FIXED_PART_SIZE: usize = std::mem::size_of::<EncryptionFlags>();
pub fn new(flags: EncryptionFlags, data_length: usize) -> Self {
Self {
flags,
data_length,
forced_long_length: false,
}
}
fn minimal_size(&self) -> usize {
Self::FIXED_PART_SIZE + per::sizeof_length(self.data_length as u16)
}
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "ironrdp-server"
version = "0.1.0"
readme = "README.md"
description = ""
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
authors.workspace = true
keywords.workspace = true
categories.workspace = true
[lib]
doctest = true
test = false
[dependencies]
anyhow = "1.0"
bytes = "1"
tokio = { version = "1", features = ["macros"] }
tokio-rustls = "0.24"
async-trait = "0.1"
ironrdp-pdu.workspace = true
ironrdp-tokio.workspace = true
ironrdp-acceptor.workspace = true
ironrdp-graphics.workspace = true
tracing.workspace = true

Some files were not shown because too many files have changed in this diff Show More