Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 28 additions & 17 deletions crates/ironrdp-connector/src/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,24 +173,35 @@ pub enum IoChannelPdu {
}

pub fn decode_io_channel(ctx: SendDataIndicationCtx<'_>) -> ConnectorResult<IoChannelPdu> {
// Multitransport PDUs use BasicSecurityHeader (flags:u16, flagsHi:u16) instead
// of the ShareControlHeader (totalLength:u16, pduType:u16, ...) used by all
// other IO channel PDUs. We discriminate by checking flagsHi == 0 (ShareControl
// has pduType there, which is always non-zero) and requiring flags to be a valid
// BasicSecurityHeaderFlags combination.
if ctx.user_data.len() >= BASIC_SECURITY_HEADER_SIZE {
let flags_raw = u16::from_le_bytes([ctx.user_data[0], ctx.user_data[1]]);
let flags_hi = u16::from_le_bytes([ctx.user_data[2], ctx.user_data[3]]);

if flags_hi == 0 {
if let Some(flags) = BasicSecurityHeaderFlags::from_bits(flags_raw) {
if flags.contains(BasicSecurityHeaderFlags::TRANSPORT_REQ) {
if let Ok(pdu) = decode::<MultitransportRequestPdu>(ctx.user_data) {
return Ok(IoChannelPdu::MultitransportRequest(pdu));
}
}
}
fn try_decode_multitransport(data: &[u8]) -> Option<MultitransportRequestPdu> {
if data.len() < BASIC_SECURITY_HEADER_SIZE {
return None;
}

let flags_raw = u16::from_le_bytes([data[0], data[1]]);
let flags_hi = u16::from_le_bytes([data[2], data[3]]);

// ShareControlHeader always has pduType | PROTOCOL_VERSION (>= 0x11) at bytes [2..4].
// BasicSecurityHeader has flagsHi == 0 for non-encrypted PDUs.
if flags_hi != 0 {
return None;
}

let flags = BasicSecurityHeaderFlags::from_bits(flags_raw)?;

if !flags.contains(BasicSecurityHeaderFlags::TRANSPORT_REQ) {
return None;
}

decode::<MultitransportRequestPdu>(data).ok()
}

// Multitransport PDUs use BasicSecurityHeader (flags:u16, flagsHi:u16) instead
// of the ShareControlHeader (totalLength:u16, pduType_with_version:u16, ...) used
// by all other IO channel PDUs. We discriminate by checking bytes [2..4] == 0:
// ShareControl always has pduType | PROTOCOL_VERSION (>= 0x11) at that offset.
if let Some(pdu) = try_decode_multitransport(ctx.user_data) {
return Ok(IoChannelPdu::MultitransportRequest(pdu));
}

let ctx = decode_share_control(ctx)?;
Expand Down
8 changes: 4 additions & 4 deletions crates/ironrdp-session/src/active_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,12 @@ pub enum ActiveStageOutput {
PointerBitmap(Arc<DecodedPointer>),
Terminate(GracefulDisconnectReason),
DeactivateAll(Box<ConnectionActivationSequence>),
/// Server Initiate Multitransport Request. The application should establish a
/// sideband UDP transport using the provided request parameters.
/// [2.2.15.1] Server Initiate Multitransport Request PDU
///
/// See [\[MS-RDPBCGR\] 2.2.15.1].
/// The application should establish a sideband UDP transport using the
/// provided request parameters.
///
/// [\[MS-RDPBCGR\] 2.2.15.1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/de783158-8b01-4818-8fb0-62523a5b3490
/// [2.2.15.1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/de783158-8b01-4818-8fb0-62523a5b3490
MultitransportRequest(MultitransportRequestPdu),
}

Expand Down
15 changes: 8 additions & 7 deletions crates/ironrdp-session/src/x224/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ pub enum ProcessorOutput {
///
/// [Deactivation-Reactivation Sequence]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/dfc234ce-481a-4674-9a5d-2a7bafb14432
DeactivateAll(Box<ConnectionActivationSequence>),
/// Server Initiate Multitransport Request. The application should establish a
/// sideband UDP transport using the request ID and security cookie, then send
/// a [`MultitransportResponsePdu`] back on the IO channel.
/// [2.2.15.1] Server Initiate Multitransport Request PDU
///
/// See [\[MS-RDPBCGR\] 2.2.15.1].
/// The application should establish a sideband UDP transport using the
/// request ID and security cookie, then send a [`MultitransportResponsePdu`]
/// back on the IO channel.
///
/// [\[MS-RDPBCGR\] 2.2.15.1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/de783158-8b01-4818-8fb0-62523a5b3490
/// [2.2.15.1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/de783158-8b01-4818-8fb0-62523a5b3490
/// [`MultitransportResponsePdu`]: ironrdp_pdu::rdp::multitransport::MultitransportResponsePdu
MultitransportRequest(MultitransportRequestPdu),
}
Expand Down Expand Up @@ -183,8 +183,9 @@ impl Processor {
}
ironrdp_connector::legacy::IoChannelPdu::MultitransportRequest(pdu) => {
debug!(
"Received Initiate Multitransport Request: request_id={}",
pdu.request_id
request_id = pdu.request_id,
protocol = ?pdu.requested_protocol,
"Received Initiate Multitransport Request"
);
Ok(vec![ProcessorOutput::MultitransportRequest(pdu)])
}
Expand Down
5 changes: 3 additions & 2 deletions ffi/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,9 @@ pub mod ffi {

/// Returns the multitransport request ID and requested protocol.
///
/// The security cookie is intentionally not exposed — it is sensitive
/// and only needed internally for transport binding.
/// The security cookie is not yet exposed because UDP transport is not
/// currently implemented. It will be added when full multitransport
/// support is available.
#[expect(
clippy::as_conversions,
reason = "RequestedProtocol is #[repr(u16)], cast is lossless"
Expand Down
Loading