feat(rdp): remote audio and a shared folder redirected as a drive

- Audio: the RDPSND channel with IronRDP's cpal playback backend (PCM;
  Opus left out to avoid another native library). On by default for new
  RDP profiles. Building now needs the ALSA headers on Linux
  (libasound2-dev), added to CI, release and fuzz jobs and the build docs.
- Shared folder (Linux/macOS): one local folder appears on the remote
  desktop as a drive. IronRDP's native backend joins server paths onto
  the share unchecked, so it is wrapped in ConfinedDrive: create, list
  and rename paths with '..' components, NUL bytes, or that resolve
  through a symlink outside the share are refused (ACCESS_DENIED);
  tests cover the escapes.

Verified against xrdp (with FUSE): the remote read the shared file, and
writes, mkdir and rename landed in the local folder. Not verified:
audio playback (the test container has no sound module), and xrdp lists
the drive as 'ignored' and can't list its directories (an IronRDP/xrdp
naming and listing mismatch, fixed upstream for the name).
This commit is contained in:
Andy Dixon
2026-10-12 18:07:49 +01:00
parent 6476ccff28
commit b798422735
15 changed files with 478 additions and 8 deletions
+28
View File
@@ -700,6 +700,34 @@ impl ProfileEditor {
"Plain TLS logon, e.g. xrdp. Less secure: the session is set up before you authenticate.",
);
});
section(ui, "Devices", |ui| {
check(ui, "Play Remote Audio", &mut c.audio, "");
if cfg!(unix) {
label(ui, "Shared Folder");
ui.horizontal(|ui| {
if widgets::button(ui, icon::FOLDER_OPEN, "Choose…").clicked()
&& let Some(dir) = rfd::FileDialog::new().set_title("Folder to share with the remote desktop").pick_folder()
{
c.shared_folder = dir.to_string_lossy().into_owned();
}
if !c.shared_folder.is_empty() && ui.button("Stop Sharing").clicked() {
c.shared_folder.clear();
}
});
err(ui, errs, "rdp.sharedFolder");
widgets::hint(
ui,
&if c.shared_folder.is_empty() {
"Nothing is shared.".to_string()
} else {
format!(
"{} appears on the remote desktop as a drive named Nexus; the server can read and change everything in it, and nothing outside it.",
c.shared_folder
)
},
);
}
});
section(ui, "Display", |ui| {
check(ui, "Scale to Fit", &mut c.scale_to_fit, "");
num(ui, "Width (0 = fit tab)", &mut c.width, 0..=8192);