mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-14 16:31:03 +03:00
webrtc: close without an await point; do not report an unknown path as direct
- close_webrtc is no longer async (hbb_common 88f965f), so the ten call sites in port_forward and io_loop - all inside select! arms or futures the UI can abandon - can no longer be cancelled mid-teardown, which left the pc unclosable and its session entry stranded. Client's own spawn_close_webrtc went with it: the runtime-teardown guard it existed for now lives in close_detached, so both Drop paths share one implementation. - webrtc_relayed() returns None when no candidate pair is selected or the pc closed under a concurrent teardown, and both call sites read that as "not relayed", i.e. direct. A TURN-relayed session could therefore be shown to the user as peer-to-peer. Claiming a direct path needs evidence of one, so an unknown answer now counts as relayed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExUfAkYbq8UC9pQCiLy8TQ
This commit is contained in:
Submodule libs/hbb_common updated: 6677318bd2...73007cb38e
@@ -202,7 +202,7 @@ impl OffererGuard {
|
|||||||
impl Drop for OffererGuard {
|
impl Drop for OffererGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(stream) = self.0.take() {
|
if let Some(stream) = self.0.take() {
|
||||||
Client::spawn_close_webrtc(stream);
|
stream.close_detached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -581,34 +581,6 @@ impl Client {
|
|||||||
!session_key.is_empty() && ice.session_key == session_key && !ice.candidate.is_empty()
|
!session_key.is_empty() && ice.session_key == session_key && !ice.candidate.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tear down an abandoned WebRTC offerer off the connection hot path.
|
|
||||||
///
|
|
||||||
/// The pc must be closed explicitly (a dropped handle leaves the `SESSIONS` clone alive),
|
|
||||||
/// but `close()` awaits internal ICE/DTLS shutdown and the result is pure cleanup with no
|
|
||||||
/// downstream dependency, so it must not block session establishment.
|
|
||||||
fn spawn_close_webrtc(webrtc: WebRTCStream) {
|
|
||||||
// Use the current runtime handle explicitly: this is also called from OffererGuard::drop,
|
|
||||||
// which can run during runtime teardown where a bare tokio::spawn would panic. If no
|
|
||||||
// runtime is available the pc cannot be closed here (it is released at process exit).
|
|
||||||
// Handle::spawn can also panic when the runtime is shutting down; catch that so Drop
|
|
||||||
// never panics (prefer a brief leak until process exit over aborting the process).
|
|
||||||
match tokio::runtime::Handle::try_current() {
|
|
||||||
Ok(handle) => {
|
|
||||||
let spawn_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
||||||
handle.spawn(async move {
|
|
||||||
webrtc.close().await;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
if spawn_result.is_err() {
|
|
||||||
log::warn!("failed to spawn WebRTC close (runtime shutting down)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
log::warn!("no tokio runtime available to close WebRTC peer connection");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether to build a WebRTC offerer for this connection.
|
/// Whether to build a WebRTC offerer for this connection.
|
||||||
///
|
///
|
||||||
/// Off by default against a private rendezvous server, like the UDP/IPv6 punch options:
|
/// Off by default against a private rendezvous server, like the UDP/IPv6 punch options:
|
||||||
@@ -1180,7 +1152,10 @@ impl Client {
|
|||||||
let direct = match typ {
|
let direct = match typ {
|
||||||
"IPv6" => true,
|
"IPv6" => true,
|
||||||
// WebRTC through a TURN server is relayed traffic; report it as such.
|
// WebRTC through a TURN server is relayed traffic; report it as such.
|
||||||
"WebRTC" => !conn.webrtc_relayed().await.unwrap_or(false),
|
// An unknown answer (no selected pair yet, or the pc closed under a
|
||||||
|
// concurrent teardown) must not be read as "direct": claiming a P2P
|
||||||
|
// path needs evidence of one.
|
||||||
|
"WebRTC" => !conn.webrtc_relayed().await.unwrap_or(true),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
// Secured and WebRTC won: disarm so the returned conn keeps the pc alive.
|
// Secured and WebRTC won: disarm so the returned conn keeps the pc alive.
|
||||||
@@ -1276,7 +1251,7 @@ impl Client {
|
|||||||
// Bailing before connect(): an offerer already adopted into webrtc_for_connect was
|
// Bailing before connect(): an offerer already adopted into webrtc_for_connect was
|
||||||
// disarmed out of its guard, so close it (and stop its bridge) explicitly here.
|
// disarmed out of its guard, so close it (and stop its bridge) explicitly here.
|
||||||
if let Some(webrtc) = webrtc_for_connect.take() {
|
if let Some(webrtc) = webrtc_for_connect.take() {
|
||||||
Self::spawn_close_webrtc(webrtc);
|
webrtc.close_detached();
|
||||||
}
|
}
|
||||||
if let Some(stop) = webrtc_bridge_stop.take() {
|
if let Some(stop) = webrtc_bridge_stop.take() {
|
||||||
let _ = stop.send(());
|
let _ = stop.send(());
|
||||||
@@ -1536,8 +1511,9 @@ impl Client {
|
|||||||
};
|
};
|
||||||
if typ == "WebRTC" {
|
if typ == "WebRTC" {
|
||||||
// WebRTC through a TURN server (force_relay, or a TURN pair winning under All
|
// WebRTC through a TURN server (force_relay, or a TURN pair winning under All
|
||||||
// policy) is relayed traffic; report the direct flag accordingly.
|
// policy) is relayed traffic; report the direct flag accordingly. An unknown answer
|
||||||
if conn.webrtc_relayed().await.unwrap_or(false) {
|
// counts as relayed — claiming a P2P path needs evidence of one.
|
||||||
|
if conn.webrtc_relayed().await.unwrap_or(true) {
|
||||||
direct = false;
|
direct = false;
|
||||||
}
|
}
|
||||||
// Secured: disarm so the returned conn keeps the pc alive.
|
// Secured: disarm so the returned conn keeps the pc alive.
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
self.handle_disconnected(round);
|
self.handle_disconnected(round);
|
||||||
// Close the WebRTC pc on this decline path too (no-op for TCP/WS); otherwise its
|
// Close the WebRTC pc on this decline path too (no-op for TCP/WS); otherwise its
|
||||||
// pc lingers in the global session cache until ICE decays on its own.
|
// pc lingers in the global session cache until ICE decays on its own.
|
||||||
peer.close_webrtc().await;
|
peer.close_webrtc();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.handler.update_direct(Some(direct));
|
self.handler.update_direct(Some(direct));
|
||||||
@@ -348,7 +348,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
// Close the WebRTC peer connection (if this session used it) so its pc is not left
|
// Close the WebRTC peer connection (if this session used it) so its pc is not left
|
||||||
// lingering in the global session cache after the session ends; dropping `peer`
|
// lingering in the global session cache after the session ends; dropping `peer`
|
||||||
// alone does not release it. No-op for TCP/WebSocket transports.
|
// alone does not release it. No-op for TCP/WebSocket transports.
|
||||||
peer.close_webrtc().await;
|
peer.close_webrtc();
|
||||||
// Stop client audio server.
|
// Stop client audio server.
|
||||||
if let Some(s) = self.stop_voice_call_sender.take() {
|
if let Some(s) = self.stop_voice_call_sender.take() {
|
||||||
s.send(()).ok();
|
s.send(()).ok();
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ async fn connect_and_login(
|
|||||||
*close_port_forward = true;
|
*close_port_forward = true;
|
||||||
// Close the WebRTC pc on this decline path too (no-op for TCP/WS), matching every
|
// Close the WebRTC pc on this decline path too (no-op for TCP/WS), matching every
|
||||||
// other exit in this function; a bare drop leaks it in the global session cache.
|
// other exit in this function; a bare drop leaks it in the global session cache.
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ async fn connect_and_login(
|
|||||||
tokio::select! {
|
tokio::select! {
|
||||||
res = timeout(READ_TIMEOUT, stream.next()) => match res {
|
res = timeout(READ_TIMEOUT, stream.next()) => match res {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
bail!("Timeout");
|
bail!("Timeout");
|
||||||
}
|
}
|
||||||
Ok(Some(Ok(bytes))) => {
|
Ok(Some(Ok(bytes))) => {
|
||||||
@@ -181,7 +181,7 @@ async fn connect_and_login(
|
|||||||
let msg_in = match Message::parse_from_bytes(&bytes) {
|
let msg_in = match Message::parse_from_bytes(&bytes) {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => msg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -194,7 +194,7 @@ async fn connect_and_login(
|
|||||||
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
||||||
Some(login_response::Union::Error(err)) => {
|
Some(login_response::Union::Error(err)) => {
|
||||||
if !interface.handle_login_error(&err) {
|
if !interface.handle_login_error(&err) {
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,11 +211,11 @@ async fn connect_and_login(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(Err(err))) => {
|
Ok(Some(Err(err))) => {
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
bail!("Connection closed: {}", err);
|
bail!("Connection closed: {}", err);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
bail!("Reset by the peer");
|
bail!("Reset by the peer");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -234,7 +234,7 @@ async fn connect_and_login(
|
|||||||
if let Some(Ok(bytes)) = res {
|
if let Some(Ok(bytes)) = res {
|
||||||
buffer.extend(bytes);
|
buffer.extend(bytes);
|
||||||
} else {
|
} else {
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -269,6 +269,6 @@ async fn run_forward(forward: Framed<TcpStream, BytesCodec>, stream: Stream) ->
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.close_webrtc().await;
|
stream.close_webrtc();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user