Skip to content
Open
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
18 changes: 13 additions & 5 deletions crates/ironrdp-dvc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,19 @@ impl DrdynvcClient {

fn process_data(&mut self, data: DrdynvcDataPdu) -> PduResult<Vec<SvcMessage>> {
let channel_id = data.channel_id();
let messages = self
.dynamic_channels
.get_by_channel_id_mut(channel_id)
.ok_or_else(|| pdu_other_err!("access to non existing DVC channel"))?
.process(data)?;
let Some(channel) = self.dynamic_channels.get_by_channel_id_mut(channel_id) else {
// A server can send data on a channel before it sees the client decline it in the
// Create Response (GNOME Remote Desktop does this for AUDIO_PLAYBACK_DVC). The data has
// nowhere to go; dropping it is enough, ending the session over it is not warranted.
// Logged at debug, as `DrdynvcServer` logs a declined channel: it is not a fault, and a
// server may keep sending such data for as long as the session lasts.
debug!(
channel_id,
"Dropping data for a dynamic virtual channel that is not open"
);
return Ok(Vec::new());
};
let messages = channel.process(data)?;

encode_dvc_messages(channel_id, messages, ChannelFlags::empty()).map_err(|e| encode_err!(e))
}
Expand Down
35 changes: 35 additions & 0 deletions crates/ironrdp-testsuite-core/tests/dvc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,41 @@ fn exchange_capabilities(client: &mut DrdynvcClient) {
client.process(&caps).expect("Capabilities Request should be processed");
}

#[test]
fn data_for_a_channel_that_is_not_open_is_dropped() {
// A server can send data on a channel before it sees the client decline it in the Create
// Response (GNOME Remote Desktop does this for AUDIO_PLAYBACK_DVC). The session must survive it.
let mut client = DrdynvcClient::new();
client
.attach_established_dynamic_channel(7, RecordedDvc::default())
.expect("recorded channel should attach");
let data = |channel_id: u32, payload: &[u8]| {
encode_vec(&DrdynvcServerPdu::Data(DrdynvcDataPdu::Data(DataPdu::new(
channel_id,
payload.to_vec(),
))))
.expect("DVC data should encode")
};

assert!(
client
.process(&data(4, b"declined"))
.expect("data for a channel that is not open should be dropped, not fail")
.is_empty()
);
assert!(
client
.process(&data(7, b"open"))
.expect("open channels are unaffected")
.is_empty()
);

let channel = client
.get_dvc_by_channel_id::<RecordedDvc>(7)
.expect("recorded channel should remain attached");
assert_eq!(channel.processor().received, vec![b"open".to_vec()]);
}

#[test]
fn established_dynamic_channel_routes_recorded_data_without_negotiation() {
let mut client = DrdynvcClient::new();
Expand Down
Loading