feat(rdp): title the mstsc window after the peer instead of "localhost" (#15781)

* feat(rdp): title the mstsc window after the peer instead of "localhost"

The RDP tunnel launched `mstsc /v:localhost:<port>`, so with several
sessions open every window is titled "localhost" and servers cannot be
told apart.

mstsc titles the session window after the launched .rdp file's base
name, so write a temp .rdp file (containing only the tunnel address)
named after the peer alias, cached hostname, or id, and launch that
instead. Falls back to the old /v: form when no usable name remains
after filename sanitization or the file cannot be written. Credential
handling is unchanged: cmdkey targets "localhost", which is still the
host mstsc resolves credentials against.

Fixes rustdesk/rustdesk#15775 (discussion)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(rdp): set mstsc title without temporary files

  Keep launching mstsc with /v so Default.rdp settings are preserved
  and unsigned RDP file warnings and policy restrictions are avoided.

  Track the launched mstsc process and reapply the peer name when the
  window title is reset during connection or reconnection.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* docs(rdp): clarify mstsc title limitation

Signed-off-by: 21pages <sunboeasy@gmail.com>

* feat(rdp): show peer identity with hostname in mstsc title

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
RustDesk
2026-08-17 13:30:18 +08:00
committed by GitHub
parent edd0e5fbd4
commit 5a78be03e3
2 changed files with 117 additions and 5 deletions

View File

@@ -15,7 +15,7 @@ use hbb_common::{
ResultType, Stream,
};
fn run_rdp(port: u16) {
fn run_rdp(port: u16, name: &str) {
std::process::Command::new("cmdkey")
.arg("/delete:localhost")
.output()
@@ -35,10 +35,37 @@ fn run_rdp(port: u16) {
.output()
.ok();
}
std::process::Command::new("mstsc")
// Keep using /v instead of a generated .rdp file: mstsc then preserves the
// user's Default.rdp settings and avoids unsigned-file warnings or policies.
match std::process::Command::new("mstsc")
.arg(format!("/v:localhost:{}", port))
.spawn()
.ok();
{
Ok(child) => {
#[cfg(windows)]
crate::platform::set_rdp_window_title(child, name.to_owned());
#[cfg(not(windows))]
let _ = (child, name);
}
Err(err) => log::warn!("Failed to launch mstsc: {}", err),
}
}
// Show the peer identity with its hostname, using the ID when no alias exists.
fn rdp_display_name(lc: &Arc<RwLock<LoginConfigHandler>>, id: &str) -> String {
let lc = lc.read().unwrap();
let alias = lc
.options
.get("alias")
.map(|s| s.trim())
.unwrap_or_default();
let hostname = lc.info.hostname.trim();
let identity = if !alias.is_empty() { alias } else { id };
if hostname.is_empty() || hostname == identity {
identity.to_owned()
} else {
format!("{} ({})", identity, hostname)
}
}
pub async fn listen(
@@ -58,7 +85,7 @@ pub async fn listen(
log::info!("listening on port {:?}", addr);
let is_rdp = port == 0;
if is_rdp {
run_rdp(addr.port());
run_rdp(addr.port(), &rdp_display_name(&lc, &id));
}
let mut ui_receiver = ui_receiver;
loop {
@@ -96,7 +123,7 @@ pub async fn listen(
}
Some(Data::NewRDP) => {
println!("receive run_rdp from ui_receiver");
run_rdp(addr.port());
run_rdp(addr.port(), &rdp_display_name(&lc, &id));
}
_ => {}
}