feat: WebRTC transport racing, DTLS identity binding, and pc-leak fixes

- prefer-P2P racing (race_transports_prefer_webrtc) across punch and RelayResponse; ICE bridge with 400ms candidate resend
- controlled-side answerer and ICE routing; sign local DTLS fingerprint into SignedId, controller verifies the binding fail-closed
- fix pc leaks: close_webrtc() on insecure-decline paths (io_loop, port_forward); compute direct before disarming the offerer guard
- point hbb_common to the WebRTC data-plane commit 9f5a296

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rustdesk
2026-07-22 11:18:35 +08:00
parent 49dc85b9c2
commit c34f29dd30
8 changed files with 887 additions and 200 deletions

View File

@@ -196,6 +196,9 @@ impl<T: InvokeUiSession> Remote<T> {
tokio::time::sleep(KCP_CLOSE_REASON_FLUSH_DELAY).await;
}
self.handle_disconnected(round);
// 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.
peer.close_webrtc().await;
return;
}
self.handler.update_direct(Some(direct));
@@ -342,6 +345,10 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
log::debug!("Exit io_loop of id={}", self.handler.get_id());
// 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`
// alone does not release it. No-op for TCP/WebSocket transports.
peer.close_webrtc().await;
// Stop client audio server.
if let Some(s) = self.stop_voice_call_sender.take() {
s.send(()).ok();