mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-19 19:01:05 +03:00
ws: decouple ICE policy from force_relay — full-ICE WebRTC over WebSocket
WebSocket support folds into force_relay because a ws tunnel kills classic TCP/UDP punching — but that conflated transport necessity with relay policy, and the WebRTC decisions keyed off the merged flag: a ws client built no offerer at all without TURN, and only a Relay-only-ICE one with it. ws deployments could never reach a direct WebRTC connection, which is exactly the path they are supposed to live on. Split the flag. LoginConfigHandler now tracks policy_relay (the force-always-relay option, an explicit relay request — /r ids and retry-via-relay included — and proxy) separately; force_relay stays policy_relay || use_ws() and keeps governing the classic paths, so non-ws behavior is unchanged everywhere: - the offerer's existence and ICE policy follow policy_relay: under pure ws the offer gathers every candidate type and may go direct; under relay-by-policy it stays Relay-only ICE, TURN-gated, exactly as before; - the RelayResponse race applies the prefer-P2P window under ws (a direct ICE path is worth delaying an already-ready relay for) while policy relay keeps first-success semantics; - the request carries webrtc_all_ice (hbb_common 64b54ab) so the controlled side knows the offer is full-ICE: it answers with full ICE and no TURN requirement, while offers without the bit keep today's relay-only answer path on every version-skew combination. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExUfAkYbq8UC9pQCiLy8TQ
This commit is contained in:
@@ -689,13 +689,13 @@ impl RendezvousMediator {
|
||||
async fn spawn_webrtc_answerer(
|
||||
&self,
|
||||
ph: &PunchHole,
|
||||
force_relay: bool,
|
||||
relay_only_ice: bool,
|
||||
server: ServerPtr,
|
||||
peer_addr: SocketAddr,
|
||||
meta: ConnectionMeta,
|
||||
) -> ResultType<String> {
|
||||
let mut stream =
|
||||
WebRTCStream::new(&ph.webrtc_sdp_offer, force_relay, CONNECT_TIMEOUT).await?;
|
||||
WebRTCStream::new(&ph.webrtc_sdp_offer, relay_only_ice, CONNECT_TIMEOUT).await?;
|
||||
let answer = match stream.get_local_endpoint_trickle().await {
|
||||
Ok(answer) => answer,
|
||||
Err(e) => {
|
||||
@@ -849,14 +849,18 @@ impl RendezvousMediator {
|
||||
// and STUN bypass the proxy and leak the real IP. WebSocket mode does NOT disable it —
|
||||
// ws only tunnels the signaling/relay legs to the server, classic punching stays forced
|
||||
// to relay (`relay` above), and the answer rides the RelayResponse, leaving ICE as the
|
||||
// only P2P path there. force_relay is different: relay-only ICE is viable with TURN.
|
||||
// only P2P path there. force_relay depends on why it was set: with webrtc_all_ice the
|
||||
// controller's relay is transport-forced (ws) and its offer carries every candidate
|
||||
// type, so answer with full ICE and let a direct pair form; without it the offer is
|
||||
// Relay-only ICE by policy, viable (and answerable) only through TURN.
|
||||
let webrtc_relay_only = ph.force_relay && !ph.webrtc_all_ice;
|
||||
let webrtc_viable = !ph.webrtc_sdp_offer.is_empty()
|
||||
&& !Config::is_proxy()
|
||||
&& (!ph.force_relay || WebRTCStream::has_turn_server());
|
||||
&& (!webrtc_relay_only || WebRTCStream::has_turn_server());
|
||||
let webrtc_sdp_answer = if webrtc_viable {
|
||||
self.spawn_webrtc_answerer(
|
||||
&ph,
|
||||
ph.force_relay,
|
||||
webrtc_relay_only,
|
||||
server.clone(),
|
||||
peer_addr,
|
||||
meta.clone(),
|
||||
|
||||
Reference in New Issue
Block a user