diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets.rs b/crates/ironrdp-pdu/src/rdp/capability_sets.rs index 38bb5a39..6dd189a0 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets.rs @@ -32,9 +32,9 @@ pub use self::bitmap_cache::{ BitmapCache, BitmapCacheRev2, CacheEntry, CacheFlags, CellInfo, BITMAP_CACHE_ENTRIES_NUM, }; pub use self::bitmap_codecs::{ - client_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty, EntropyBits, Guid, NsCodec, - RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags, CODEC_ID_NONE, - CODEC_ID_REMOTEFX, + client_codecs_capabilities, server_codecs_capabilities, BitmapCodecs, CaptureFlags, Codec, CodecId, CodecProperty, + EntropyBits, Guid, NsCodec, RemoteFxContainer, RfxCaps, RfxCapset, RfxClientCapsContainer, RfxICap, RfxICapFlags, + CODEC_ID_NONE, CODEC_ID_REMOTEFX, }; pub use self::brush::{Brush, SupportLevel}; pub use self::frame_acknowledge::FrameAcknowledge; diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs index 32162e94..01a7a0ee 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs @@ -2,6 +2,7 @@ mod tests; use core::fmt::{self, Debug}; +use std::collections::HashMap; use bitflags::bitflags; use ironrdp_core::{ @@ -641,6 +642,30 @@ impl CodecId { } } +fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result, String> { + let mut result = HashMap::new(); + + for &codec_str in codecs { + if let Some(colon_index) = codec_str.find(':') { + let codec_name = &codec_str[0..colon_index]; + let state_str = &codec_str[colon_index + 1..]; + + let state = match state_str { + "on" => true, + "off" => false, + _ => return Err(format!("Unhandled configuration: {state_str}")), + }; + + result.insert(codec_name, state); + } else { + // No colon found, assume it's "on" + result.insert(codec_str, true); + } + } + + Ok(result) +} + /// This function generates a list of client codec capabilities based on the /// provided configuration. /// @@ -659,32 +684,6 @@ impl CodecId { /// A vector of `Codec` structs representing the codec capabilities, or an error /// suitable for CLI. pub fn client_codecs_capabilities(config: &[&str]) -> Result { - use std::collections::HashMap; - - fn parse_codecs_config<'a>(codecs: &'a [&'a str]) -> Result, String> { - let mut result = HashMap::new(); - - for &codec_str in codecs { - if let Some(colon_index) = codec_str.find(':') { - let codec_name = &codec_str[0..colon_index]; - let state_str = &codec_str[colon_index + 1..]; - - let state = match state_str { - "on" => true, - "off" => false, - _ => return Err(format!("Unhandled configuration: {state_str}")), - }; - - result.insert(codec_name, state); - } else { - // No colon found, assume it's "on" - result.insert(codec_str, true); - } - } - - Ok(result) - } - if config.contains(&"help") { return Err(r#" List of codecs: @@ -692,6 +691,7 @@ List of codecs: "# .to_owned()); } + let mut config = parse_codecs_config(config)?; let mut codecs = vec![]; @@ -715,3 +715,51 @@ List of codecs: Ok(BitmapCodecs(codecs)) } + +/// This function generates a list of server codec capabilities based on the +/// provided configuration. +/// +/// # Arguments +/// +/// * `config` - A slice of string slices that specifies which codecs to include +/// in the capabilities. Codecs can be explicitly turned on ("codec:on") or +/// off ("codec:off"). +/// +/// # List of codecs +/// +/// * `remotefx` (on by default) +/// +/// # Returns +/// +/// A vector of `Codec` structs representing the codec capabilities, or an help message suitable +/// for CLI errors. +pub fn server_codecs_capabilities(config: &[&str]) -> Result { + if config.contains(&"help") { + return Err(r#" +List of codecs: +- `remotefx` (on by default) +"# + .to_owned()); + } + + let mut config = parse_codecs_config(config)?; + let mut codecs = vec![]; + + if config.remove("remotefx").unwrap_or(true) { + codecs.push(Codec { + id: 0, + property: CodecProperty::RemoteFx(RemoteFxContainer::ServerContainer(1)), + }); + codecs.push(Codec { + id: 0, + property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(1)), + }); + } + + let codec_names = config.keys().copied().collect::>().join(", "); + if !codec_names.is_empty() { + return Err(format!("Unknown codecs: {codec_names}")); + } + + Ok(BitmapCodecs(codecs)) +} diff --git a/crates/ironrdp-server/src/builder.rs b/crates/ironrdp-server/src/builder.rs index d31aedba..c8e7b9e2 100644 --- a/crates/ironrdp-server/src/builder.rs +++ b/crates/ironrdp-server/src/builder.rs @@ -1,6 +1,7 @@ use core::net::SocketAddr; use anyhow::Result; +use ironrdp_pdu::rdp::capability_sets::{server_codecs_capabilities, BitmapCodecs}; use tokio_rustls::TlsAcceptor; use super::clipboard::CliprdrServerFactory; @@ -25,7 +26,7 @@ pub struct WantsDisplay { pub struct BuilderDone { addr: SocketAddr, security: RdpServerSecurity, - with_remote_fx: bool, + codecs: BitmapCodecs, handler: Box, display: Box, cliprdr_factory: Option>, @@ -124,7 +125,7 @@ impl RdpServerBuilder { display: Box::new(display), sound_factory: None, cliprdr_factory: None, - with_remote_fx: true, + codecs: server_codecs_capabilities(&[]).unwrap(), }, } } @@ -138,7 +139,7 @@ impl RdpServerBuilder { display: Box::new(NoopDisplay), sound_factory: None, cliprdr_factory: None, - with_remote_fx: true, + codecs: server_codecs_capabilities(&[]).unwrap(), }, } } @@ -155,8 +156,8 @@ impl RdpServerBuilder { self } - pub fn with_remote_fx(mut self, enabled: bool) -> Self { - self.state.with_remote_fx = enabled; + pub fn with_bitmap_codecs(mut self, codecs: BitmapCodecs) -> Self { + self.state.codecs = codecs; self } @@ -165,7 +166,7 @@ impl RdpServerBuilder { RdpServerOptions { addr: self.state.addr, security: self.state.security, - with_remote_fx: self.state.with_remote_fx, + codecs: self.state.codecs, }, self.state.handler, self.state.display, diff --git a/crates/ironrdp-server/src/capabilities.rs b/crates/ironrdp-server/src/capabilities.rs index 0e2df934..5a7cc8ea 100644 --- a/crates/ironrdp-server/src/capabilities.rs +++ b/crates/ironrdp-server/src/capabilities.rs @@ -12,7 +12,7 @@ pub(crate) fn capabilities(opts: &RdpServerOptions, size: DesktopSize) -> Vec capability_sets::MultifragmentUpdate { max_request_size: 16_777_215, } } - -fn bitmap_codecs(with_remote_fx: bool) -> capability_sets::BitmapCodecs { - let mut codecs = Vec::new(); - if with_remote_fx { - codecs.push(capability_sets::Codec { - id: 0, - property: capability_sets::CodecProperty::RemoteFx(capability_sets::RemoteFxContainer::ServerContainer(1)), - }); - codecs.push(capability_sets::Codec { - id: 0, - property: capability_sets::CodecProperty::ImageRemoteFx( - capability_sets::RemoteFxContainer::ServerContainer(1), - ), - }); - } - capability_sets::BitmapCodecs(codecs) -} diff --git a/crates/ironrdp-server/src/server.rs b/crates/ironrdp-server/src/server.rs index c2a663a0..0acb7603 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -13,7 +13,7 @@ use ironrdp_displaycontrol::server::{DisplayControlHandler, DisplayControlServer use ironrdp_pdu::input::fast_path::{FastPathInput, FastPathInputEvent}; use ironrdp_pdu::input::InputEventPdu; use ironrdp_pdu::mcs::{SendDataIndication, SendDataRequest}; -use ironrdp_pdu::rdp::capability_sets::{BitmapCodecs, CapabilitySet, CmdFlags, GeneralExtraFlags}; +use ironrdp_pdu::rdp::capability_sets::{BitmapCodecs, CapabilitySet, CmdFlags, CodecProperty, GeneralExtraFlags}; pub use ironrdp_pdu::rdp::client_info::Credentials; use ironrdp_pdu::rdp::headers::{ServerDeactivateAll, ShareControlPdu}; use ironrdp_pdu::x224::X224; @@ -38,7 +38,23 @@ use crate::{builder, capabilities, SoundServerFactory}; pub struct RdpServerOptions { pub addr: SocketAddr, pub security: RdpServerSecurity, - pub with_remote_fx: bool, + pub codecs: BitmapCodecs, +} + +impl RdpServerOptions { + fn has_image_remote_fx(&self) -> bool { + self.codecs + .0 + .iter() + .any(|codec| matches!(codec.property, CodecProperty::ImageRemoteFx(_))) + } + + fn has_remote_fx(&self) -> bool { + self.codecs + .0 + .iter() + .any(|codec| matches!(codec.property, CodecProperty::RemoteFx(_))) + } } #[derive(Clone)] @@ -711,21 +727,21 @@ impl RdpServer { // We should distinguish parameters for both modes, // and somehow choose the "best", instead of picking // the last parsed here. - rdp::capability_sets::CodecProperty::RemoteFx( - rdp::capability_sets::RemoteFxContainer::ClientContainer(c), - ) if self.opts.with_remote_fx => { + CodecProperty::RemoteFx(rdp::capability_sets::RemoteFxContainer::ClientContainer(c)) + if self.opts.has_remote_fx() => + { for caps in c.caps_data.0 .0 { update_codecs.set_remotefx(Some((caps.entropy_bits, codec.id))); } } - rdp::capability_sets::CodecProperty::ImageRemoteFx( - rdp::capability_sets::RemoteFxContainer::ClientContainer(c), - ) if self.opts.with_remote_fx => { + CodecProperty::ImageRemoteFx(rdp::capability_sets::RemoteFxContainer::ClientContainer( + c, + )) if self.opts.has_image_remote_fx() => { for caps in c.caps_data.0 .0 { update_codecs.set_remotefx(Some((caps.entropy_bits, codec.id))); } } - rdp::capability_sets::CodecProperty::NsCodec(_) => (), + CodecProperty::NsCodec(_) => (), _ => (), } }