mirror of
https://github.com/rustdesk/hbb_common.git
synced 2026-08-27 04:37:35 +00:00
webrtc: declare the ICE policy inside the offer envelope, not a proto field
Reverts the webrtc_all_ice proto field (64b54ab) in favor of an
`ice_policy: "all"` key inside the webrtc:// envelope JSON, next to the
RTCSessionDescription fields. Same information, better carrier:
- it is a property of the offer itself, so it rides with the offer;
- the rendezvous server never has to know: the envelope is an opaque,
length-bounded string to hbbs, so no forwarding code and no vendored
proto copies to keep in sync;
- serde ignores unknown JSON keys when parsing RTCSessionDescription,
so every skew combination degrades exactly like the proto field did:
absence - not an error - is the old Relay-only reading.
endpoint_declares_all_ice() is the receiving side: parse failure,
foreign scheme or missing key all read as "not declared".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExUfAkYbq8UC9pQCiLy8TQ
This commit is contained in:
@@ -29,13 +29,10 @@ message PunchHoleRequest {
|
|||||||
int32 upnp_port = 9;
|
int32 upnp_port = 9;
|
||||||
bytes socket_addr_v6 = 10;
|
bytes socket_addr_v6 = 10;
|
||||||
string switch_code = 11;
|
string switch_code = 11;
|
||||||
|
// The offer's envelope declares its own ICE transport policy (`ice_policy` key inside
|
||||||
|
// the webrtc:// payload): under force_relay it tells the peer whether the relay is
|
||||||
|
// transport-forced (WebSocket — answer may use full ICE) or policy (Relay-only + TURN).
|
||||||
string webrtc_sdp_offer = 12;
|
string webrtc_sdp_offer = 12;
|
||||||
// The attached offer gathers every ICE candidate type (host/srflx/relay), so a direct
|
|
||||||
// WebRTC path may form even when force_relay is set. Sent by clients whose force_relay
|
|
||||||
// stems from the transport (WebSocket tunnels only the signaling/relay legs and kills
|
|
||||||
// classic punching, not ICE) rather than from relay-by-policy; absent/false keeps the
|
|
||||||
// old semantics where force_relay implies a Relay-only-ICE offer.
|
|
||||||
bool webrtc_all_ice = 13;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ControlPermissions {
|
message ControlPermissions {
|
||||||
@@ -72,9 +69,6 @@ message PunchHole {
|
|||||||
ControlPermissions control_permissions = 8;
|
ControlPermissions control_permissions = 8;
|
||||||
ControlledContext controlled_context = 9;
|
ControlledContext controlled_context = 9;
|
||||||
string webrtc_sdp_offer = 10;
|
string webrtc_sdp_offer = 10;
|
||||||
// Forwarded from PunchHoleRequest.webrtc_all_ice; see that field. Tag 11 previously
|
|
||||||
// carried the never-shipped `requester_id` (added and removed in one unshipped batch).
|
|
||||||
bool webrtc_all_ice = 11;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TestNatRequest {
|
message TestNatRequest {
|
||||||
|
|||||||
+61
-1
@@ -186,6 +186,11 @@ impl WebRTCStream {
|
|||||||
format!("webrtc://{}", encoded_sdp)
|
format!("webrtc://{}", encoded_sdp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Envelope JSON key carrying the local description's ICE transport policy, alongside the
|
||||||
|
// RTCSessionDescription fields (see `get_local_endpoint_trickle`).
|
||||||
|
const ICE_POLICY_KEY: &str = "ice_policy";
|
||||||
|
const ICE_POLICY_ALL: &str = "all";
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_key_for_sdp(sdp: &RTCSessionDescription) -> ResultType<String> {
|
fn get_key_for_sdp(sdp: &RTCSessionDescription) -> ResultType<String> {
|
||||||
let binding = sdp.unmarshal()?;
|
let binding = sdp.unmarshal()?;
|
||||||
@@ -601,7 +606,21 @@ impl WebRTCStream {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub async fn get_local_endpoint_trickle(&self) -> ResultType<String> {
|
pub async fn get_local_endpoint_trickle(&self) -> ResultType<String> {
|
||||||
if let Some(local_desc) = self.pc.local_description().await {
|
if let Some(local_desc) = self.pc.local_description().await {
|
||||||
let sdp = serde_json::to_string(&local_desc)?;
|
let sdp = if self.relay_only {
|
||||||
|
serde_json::to_string(&local_desc)?
|
||||||
|
} else {
|
||||||
|
// Declare the ICE transport policy inside the envelope. The receiver of an
|
||||||
|
// offer that arrives with force_relay set must know whether it may answer
|
||||||
|
// with full ICE (relay forced by the transport, e.g. WebSocket signaling)
|
||||||
|
// or must stay Relay-only + TURN-gated (relay by policy) — and the envelope
|
||||||
|
// is the offer's own property, so it rides here rather than in a proto
|
||||||
|
// field the rendezvous server would have to forward. An extra key is
|
||||||
|
// invisible to older peers: serde ignores unknown fields when parsing
|
||||||
|
// RTCSessionDescription, so absence — not an error — is the old semantics.
|
||||||
|
let mut v = serde_json::to_value(&local_desc)?;
|
||||||
|
v[Self::ICE_POLICY_KEY] = serde_json::Value::from(Self::ICE_POLICY_ALL);
|
||||||
|
serde_json::to_string(&v)?
|
||||||
|
};
|
||||||
let endpoint = Self::sdp_to_endpoint(&sdp);
|
let endpoint = Self::sdp_to_endpoint(&sdp);
|
||||||
Ok(endpoint)
|
Ok(endpoint)
|
||||||
} else {
|
} else {
|
||||||
@@ -609,6 +628,24 @@ impl WebRTCStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the peer's endpoint declares it was built with ICE transport policy `all`
|
||||||
|
/// (W3C RTCIceTransportPolicy), i.e. it gathers host/srflx/relay candidates and a direct
|
||||||
|
/// pair may form even though the request carries force_relay. Absent key, foreign format
|
||||||
|
/// or parse failure all mean "not declared" — the old Relay-only reading.
|
||||||
|
pub fn endpoint_declares_all_ice(endpoint: &str) -> bool {
|
||||||
|
let Ok(sdp_json) = Self::get_remote_offer(endpoint) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
serde_json::from_str::<serde_json::Value>(&sdp_json)
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| {
|
||||||
|
v.get(Self::ICE_POLICY_KEY)?
|
||||||
|
.as_str()
|
||||||
|
.map(|p| p == Self::ICE_POLICY_ALL)
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn set_remote_endpoint(&self, endpoint: &str) -> ResultType<()> {
|
pub async fn set_remote_endpoint(&self, endpoint: &str) -> ResultType<()> {
|
||||||
let offer = Self::get_remote_offer(endpoint)?;
|
let offer = Self::get_remote_offer(endpoint)?;
|
||||||
@@ -1037,6 +1074,29 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_endpoint_ice_policy_declaration() {
|
||||||
|
// An envelope with the marker declares full ICE; everything else — no marker,
|
||||||
|
// wrong value, foreign scheme, garbage — reads as the old Relay-only semantics.
|
||||||
|
let marked = WebRTCStream::sdp_to_endpoint(r#"{"type":"offer","sdp":"v=0","ice_policy":"all"}"#);
|
||||||
|
assert!(WebRTCStream::endpoint_declares_all_ice(&marked));
|
||||||
|
|
||||||
|
let unmarked = WebRTCStream::sdp_to_endpoint(r#"{"type":"offer","sdp":"v=0"}"#);
|
||||||
|
assert!(!WebRTCStream::endpoint_declares_all_ice(&unmarked));
|
||||||
|
|
||||||
|
let wrong = WebRTCStream::sdp_to_endpoint(r#"{"type":"offer","sdp":"v=0","ice_policy":"relay"}"#);
|
||||||
|
assert!(!WebRTCStream::endpoint_declares_all_ice(&wrong));
|
||||||
|
|
||||||
|
assert!(!WebRTCStream::endpoint_declares_all_ice(""));
|
||||||
|
assert!(!WebRTCStream::endpoint_declares_all_ice("webrtc://not-base64!"));
|
||||||
|
assert!(!WebRTCStream::endpoint_declares_all_ice("https://example.com"));
|
||||||
|
|
||||||
|
// The marker must be invisible to the plain RTCSessionDescription parse old peers do.
|
||||||
|
let sdp_json = WebRTCStream::get_remote_offer(&marked).unwrap();
|
||||||
|
serde_json::from_str::<webrtc::peer_connection::sdp::session_description::RTCSessionDescription>(&sdp_json)
|
||||||
|
.expect("extra envelope key must not break RTCSessionDescription parsing");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_webrtc_session_key() {
|
fn test_webrtc_session_key() {
|
||||||
let mut sdp_str = "".to_owned();
|
let mut sdp_str = "".to_owned();
|
||||||
|
|||||||
Reference in New Issue
Block a user