diff --git a/src/stream.rs b/src/stream.rs index ac1be3040..2df826915 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -122,6 +122,19 @@ impl Stream { } } + /// Whether an established WebRTC transport reaches the peer over IPv6 (used to name the + /// transport in the UI). `None` for non-WebRTC transports and before ICE selects a pair — + /// every other transport already carries the family in the label it was raced under. + #[inline] + pub async fn webrtc_remote_ipv6(&self) -> Option { + match self { + #[cfg(feature = "webrtc")] + Stream::WebRTC(s) => s.is_remote_ipv6().await, + #[allow(unreachable_patterns)] + _ => None, + } + } + /// DTLS certificate fingerprint for a WebRTC stream (`local`=true for this endpoint's own /// cert, false for the peer's), used to bind the channel to the signed peer identity. /// Returns None for non-WebRTC transports, which authenticate via the secretbox key exchange. diff --git a/src/webrtc.rs b/src/webrtc.rs index 0de1f3501..a7bda1c65 100644 --- a/src/webrtc.rs +++ b/src/webrtc.rs @@ -1020,6 +1020,20 @@ impl WebRTCStream { ) } + /// Whether the nominated pair reaches the peer over IPv6 — `None` before one is selected. + /// The remote side on purpose: it is the address the peer is actually reached at, which the + /// rendezvous-observed address the session is otherwise identified by cannot report. + pub async fn is_remote_ipv6(&self) -> Option { + let dtls = self.pc.sctp().transport(); + let pair = dtls.ice_transport().get_selected_candidate_pair().await?; + // Same `Display` parse as `is_relayed`, one token over: a side renders as + // `(remote)
:`, and `protocol` is only udp/tcp, so the + // family has to come from the address — an IPv6 literal is the only one with two colons. + let pair = pair.to_string(); + let remote = pair.split(" <-> ").nth(1)?; + Some(remote.split_whitespace().nth(3)?.matches(':').count() > 1) + } + #[inline] pub fn take_local_ice_rx(&self) -> Option> { self.local_ice_rx.lock().ok().and_then(|mut rx| rx.take())