From 8b7f3b8b7500a9592cfa745b4fac7d232d4fc6cf Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 4 Sep 2026 11:14:17 +0800 Subject: [PATCH] port forward: a legacy window stays legacy until it is reopened MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review: the merged `Claimed | Legacy` arm gave a legacy window a hot transition to a tunnel — every accept re-negotiated, and a peer upgraded while the window stayed open was promoted underneath live connections. The product does not need a mode switch inside a window's lifetime, and the transition was extra state-machine surface for nothing: reopening the window picks up an upgraded peer. The two arms are separate again. `Claimed` negotiates once and the peer's answer fixes the window's mode. `Legacy` logs in for every accept as before, asks for no tunnel — `LoginConfigHandler::port_forward_mux` carries the request per login, so the raw pipe never has to talk to a peer that thinks it agreed to multiplex — and ignores what the peer reports. Both arms keep skipping a local socket that hung up during login. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab --- src/client.rs | 9 ++++----- src/port_forward.rs | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/client.rs b/src/client.rs index d17f62587..a464f53a6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1753,10 +1753,9 @@ pub struct LoginConfigHandler { pub remember: bool, config: PeerConfig, pub port_forward: (String, i32), - /// Held by a port-forward mapping from filling `port_forward` and `hash` - /// until its login is built from them; a window's mappings log in - /// concurrently. - pub(crate) port_forward_login_turn: Arc>, + /// Whether the next port-forward login asks for the multiplexed tunnel. + /// `port_forward::listen` sets it per accept. + pub port_forward_mux: bool, pub version: i64, features: Option, pub session_id: u64, // used for local <-> server communication @@ -2769,7 +2768,7 @@ impl LoginConfigHandler { ConnType::PORT_FORWARD | ConnType::RDP => lr.set_port_forward(PortForward { host: self.port_forward.0.clone(), port: self.port_forward.1, - multiplex: crate::common::get_port_forward_mux_enabled(), + multiplex: self.port_forward_mux, ..Default::default() }), ConnType::TERMINAL => { diff --git a/src/port_forward.rs b/src/port_forward.rs index 9dcfce579..656fa4d8b 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -125,11 +125,14 @@ pub async fn listen( log::debug!("cannot open channel for {:?}: {}", peer_addr, e); } } - // A `Legacy` window logs in for every accept, as it always did, - // and publishes the outcome the same way the claiming accept - // does: a peer upgraded since the window opened becomes a tunnel. - Claim::Claimed | Claim::Legacy => { - lc.write().unwrap().port_forward = (remote_host.clone(), remote_port); + // The claiming accept negotiates: it asks for the tunnel, and + // the peer's answer fixes the window's mode until it closes. + Claim::Claimed => { + { + let mut lc = lc.write().unwrap(); + lc.port_forward = (remote_host.clone(), remote_port); + lc.port_forward_mux = crate::common::get_port_forward_mux_enabled(); + } let mut forward = Framed::new(forward, BytesCodec::new()); let mut close_port_forward = false; match connect_and_login(&id, &password, &mut ui_receiver, interface.clone(), &mut forward, key, token, is_rdp, &mut close_port_forward).await { @@ -161,6 +164,29 @@ pub async fn listen( _ => tunnel.set_failed(), } } + // A `Legacy` window stays legacy until it closes: every accept + // logs in on its own, asks for no tunnel, and takes the raw pipe + // whatever the peer reports. Reopening the window is how a user + // picks up an upgraded peer; nothing switches modes underneath + // live connections. + Claim::Legacy => { + { + let mut lc = lc.write().unwrap(); + lc.port_forward = (remote_host.clone(), remote_port); + lc.port_forward_mux = false; + } + let mut forward = Framed::new(forward, BytesCodec::new()); + let mut close_port_forward = false; + match connect_and_login(&id, &password, &mut ui_receiver, interface.clone(), &mut forward, key, token, is_rdp, &mut close_port_forward).await { + Ok(Some(outcome)) if outcome.local_eof => { + log::debug!("legacy peer and local {:?} already gone", peer_addr); + } + Ok(Some(outcome)) => run_legacy(outcome, forward, peer_addr, interface.clone()), + _ if close_port_forward => break, + Err(err) => interface.on_establish_connection_error(err.to_string()), + _ => {} + } + } // Resolved above; a stray `Wait` just drops this accept. Claim::Wait => continue, }