Compare commits

...

3 Commits

Author SHA1 Message Date
fufesou
a563976239 refact(port-forward): comments and logs
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-03-03 20:47:56 +08:00
fufesou
3b225b7c6c fix(security): gate port-forward connect on full auth and clarify login flow semantics
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-03-03 17:35:04 +08:00
fufesou
fc80106a8b fix(port-forward): try connect after 2fa
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-03-03 16:21:43 +08:00

View File

@@ -560,10 +560,9 @@ impl Connection {
match data { match data {
ipc::Data::Authorize => { ipc::Data::Authorize => {
conn.require_2fa.take(); conn.require_2fa.take();
if !conn.connect_port_forward_if_needed().await { if !conn.send_logon_response_and_keep_alive().await {
break; break;
} }
conn.send_logon_response().await;
if conn.port_forward_socket.is_some() { if conn.port_forward_socket.is_some() {
break; break;
} }
@@ -1358,9 +1357,10 @@ impl Connection {
if self.port_forward_socket.is_some() { if self.port_forward_socket.is_some() {
return true; return true;
} }
let Some(login_request::Union::PortForward(mut pf)) = self.lr.union.clone() else { let Some(login_request::Union::PortForward(pf)) = self.lr.union.as_ref() else {
return true; return true;
}; };
let mut pf = pf.clone();
let (mut addr, is_rdp) = Self::normalize_port_forward_target(&mut pf); let (mut addr, is_rdp) = Self::normalize_port_forward_target(&mut pf);
self.port_forward_address = addr.clone(); self.port_forward_address = addr.clone();
match timeout(3000, TcpStream::connect(&addr)).await { match timeout(3000, TcpStream::connect(&addr)).await {
@@ -1368,12 +1368,25 @@ impl Connection {
self.port_forward_socket = Some(Framed::new(sock, BytesCodec::new())); self.port_forward_socket = Some(Framed::new(sock, BytesCodec::new()));
true true
} }
_ => { Ok(Err(e)) => {
log::warn!("Port forward connect failed for {}: {}", addr, e);
if is_rdp { if is_rdp {
addr = "RDP".to_owned(); addr = "RDP".to_owned();
} }
self.send_login_error(format!( self.send_login_error(format!(
"Failed to access remote {}, please make sure if it is open", "Failed to access remote {}. Please make sure it is reachable/open.",
addr
))
.await;
false
}
Err(e) => {
log::warn!("Port forward connect timed out for {}: {}", addr, e);
if is_rdp {
addr = "RDP".to_owned();
}
self.send_login_error(format!(
"Failed to access remote {}. Please make sure it is reachable/open.",
addr addr
)) ))
.await; .await;
@@ -1382,9 +1395,11 @@ impl Connection {
} }
} }
async fn send_logon_response(&mut self) { // Returns whether this connection should be kept alive.
// `true` does not necessarily mean authorization succeeded (e.g. REQUIRE_2FA case).
async fn send_logon_response_and_keep_alive(&mut self) -> bool {
if self.authorized { if self.authorized {
return; return true;
} }
if self.require_2fa.is_some() && !self.is_recent_session(true) && !self.from_switch { if self.require_2fa.is_some() && !self.is_recent_session(true) && !self.from_switch {
self.require_2fa.as_ref().map(|totp| { self.require_2fa.as_ref().map(|totp| {
@@ -1415,7 +1430,11 @@ impl Connection {
} }
}); });
self.send_login_error(crate::client::REQUIRE_2FA).await; self.send_login_error(crate::client::REQUIRE_2FA).await;
return; // Keep the connection alive so the client can continue with 2FA.
return true;
}
if !self.connect_port_forward_if_needed().await {
return false;
} }
self.authorized = true; self.authorized = true;
let (conn_type, auth_conn_type) = if self.file_transfer.is_some() { let (conn_type, auth_conn_type) = if self.file_transfer.is_some() {
@@ -1538,7 +1557,7 @@ impl Connection {
res.set_peer_info(pi); res.set_peer_info(pi);
msg_out.set_login_response(res); msg_out.set_login_response(res);
self.send(msg_out).await; self.send(msg_out).await;
return; return true;
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
if self.is_remote() { if self.is_remote() {
@@ -1561,7 +1580,7 @@ impl Connection {
let mut msg_out = Message::new(); let mut msg_out = Message::new();
msg_out.set_login_response(res); msg_out.set_login_response(res);
self.send(msg_out).await; self.send(msg_out).await;
return; return true;
} }
} }
#[allow(unused_mut)] #[allow(unused_mut)]
@@ -1715,6 +1734,7 @@ impl Connection {
self.try_sub_monitor_services(); self.try_sub_monitor_services();
} }
} }
true
} }
fn try_sub_camera_displays(&mut self) { fn try_sub_camera_displays(&mut self) {
@@ -2254,9 +2274,7 @@ impl Connection {
// `is_logon_ui()` is a fallback for logon UI detection on Windows. // `is_logon_ui()` is a fallback for logon UI detection on Windows.
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let is_logon = || { let is_logon = || {
crate::platform::is_prelogin() crate::platform::is_prelogin() || crate::platform::is_locked() || {
|| crate::platform::is_locked()
|| {
match crate::platform::is_logon_ui() { match crate::platform::is_logon_ui() {
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
@@ -2295,10 +2313,9 @@ impl Connection {
if err_msg.is_empty() { if err_msg.is_empty() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
self.linux_headless_handle.wait_desktop_cm_ready().await; self.linux_headless_handle.wait_desktop_cm_ready().await;
if !self.connect_port_forward_if_needed().await { if !self.send_logon_response_and_keep_alive().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), self.authorized); self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), self.authorized);
} else { } else {
self.send_login_error(err_msg).await; self.send_login_error(err_msg).await;
@@ -2334,10 +2351,9 @@ impl Connection {
if err_msg.is_empty() { if err_msg.is_empty() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
self.linux_headless_handle.wait_desktop_cm_ready().await; self.linux_headless_handle.wait_desktop_cm_ready().await;
if !self.connect_port_forward_if_needed().await { if !self.send_logon_response_and_keep_alive().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm(lr.my_id, lr.my_name, self.authorized); self.try_start_cm(lr.my_id, lr.my_name, self.authorized);
} else { } else {
self.send_login_error(err_msg).await; self.send_login_error(err_msg).await;
@@ -2355,10 +2371,9 @@ impl Connection {
self.update_failure(failure, true, 1); self.update_failure(failure, true, 1);
self.require_2fa.take(); self.require_2fa.take();
raii::AuthedConnID::set_session_2fa(self.session_key()); raii::AuthedConnID::set_session_2fa(self.session_key());
if !self.connect_port_forward_if_needed().await { if !self.send_logon_response_and_keep_alive().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm( self.try_start_cm(
self.lr.my_id.to_owned(), self.lr.my_id.to_owned(),
self.lr.my_name.to_owned(), self.lr.my_name.to_owned(),
@@ -2409,10 +2424,9 @@ impl Connection {
if let Some((_instant, uuid_old)) = uuid_old { if let Some((_instant, uuid_old)) = uuid_old {
if uuid == uuid_old { if uuid == uuid_old {
self.from_switch = true; self.from_switch = true;
if !self.connect_port_forward_if_needed().await { if !self.send_logon_response_and_keep_alive().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm( self.try_start_cm(
lr.my_id.clone(), lr.my_id.clone(),
lr.my_name.clone(), lr.my_name.clone(),
@@ -5378,9 +5392,8 @@ mod raii {
} }
pub fn check_wake_lock_on_setting_changed() { pub fn check_wake_lock_on_setting_changed() {
let current = config::Config::get_bool_option( let current =
keys::OPTION_KEEP_AWAKE_DURING_INCOMING_SESSIONS, config::Config::get_bool_option(keys::OPTION_KEEP_AWAKE_DURING_INCOMING_SESSIONS);
);
let cached = *WAKELOCK_KEEP_AWAKE_OPTION.lock().unwrap(); let cached = *WAKELOCK_KEEP_AWAKE_OPTION.lock().unwrap();
if cached != Some(current) { if cached != Some(current) {
Self::check_wake_lock(); Self::check_wake_lock();