mirror of
https://github.com/rustdesk/hbb_common.git
synced 2026-02-16 02:20:43 +00:00
support turn relay config, and force_relay option
This commit is contained in:
@@ -70,7 +70,7 @@ async fn main() -> Result<()> {
|
|||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let webrtc_stream = WebRTCStream::new(&remote_endpoint, 30000).await?;
|
let webrtc_stream = WebRTCStream::new(&remote_endpoint, false, 30000).await?;
|
||||||
// Print the offer to be sent to the other peer
|
// Print the offer to be sent to the other peer
|
||||||
let local_endpoint = webrtc_stream.get_local_endpoint().await?;
|
let local_endpoint = webrtc_stream.get_local_endpoint().await?;
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ pub async fn connect_tcp<
|
|||||||
#[cfg(feature = "webrtc")]
|
#[cfg(feature = "webrtc")]
|
||||||
if is_webrtc_endpoint(&target.to_string()) {
|
if is_webrtc_endpoint(&target.to_string()) {
|
||||||
return Ok(Stream::WebRTC(
|
return Ok(Stream::WebRTC(
|
||||||
webrtc::WebRTCStream::new(&target.to_string(), ms_timeout).await?,
|
webrtc::WebRTCStream::new(&target.to_string(), false, ms_timeout).await?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let target_str = check_ws(&target.to_string());
|
let target_str = check_ws(&target.to_string());
|
||||||
|
|||||||
122
src/webrtc.rs
122
src/webrtc.rs
@@ -11,6 +11,7 @@ use webrtc::ice::mdns::MulticastDnsMode;
|
|||||||
use webrtc::ice_transport::ice_server::RTCIceServer;
|
use webrtc::ice_transport::ice_server::RTCIceServer;
|
||||||
use webrtc::peer_connection::configuration::RTCConfiguration;
|
use webrtc::peer_connection::configuration::RTCConfiguration;
|
||||||
use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState;
|
use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState;
|
||||||
|
use webrtc::peer_connection::policy::ice_transport_policy::RTCIceTransportPolicy;
|
||||||
use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;
|
use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;
|
||||||
use webrtc::peer_connection::RTCPeerConnection;
|
use webrtc::peer_connection::RTCPeerConnection;
|
||||||
|
|
||||||
@@ -20,7 +21,9 @@ use bytes::{Bytes, BytesMut};
|
|||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
use crate::protobuf::Message;
|
use crate::protobuf::Message;
|
||||||
use crate::sodiumoxide::crypto::secretbox::Key;
|
use crate::sodiumoxide::crypto::secretbox::Key;
|
||||||
use crate::ResultType;
|
use crate::ResultType;
|
||||||
@@ -36,7 +39,15 @@ pub struct WebRTCStream {
|
|||||||
/// Most browsers, including Chromium, enforce this protocol limit.
|
/// Most browsers, including Chromium, enforce this protocol limit.
|
||||||
const DATA_CHANNEL_BUFFER_SIZE: u16 = u16::MAX;
|
const DATA_CHANNEL_BUFFER_SIZE: u16 = u16::MAX;
|
||||||
|
|
||||||
const DEFAULT_ICE_SERVER: &str = "stun:stun.cloudflare.com:3478";
|
// use 3 public STUN servers to find out the NAT type, 2 must be the same address but different ports
|
||||||
|
// https://stackoverflow.com/questions/72805316/determine-nat-mapping-behaviour-using-two-stun-servers
|
||||||
|
// luckily nextcloud supports two ports for STUN
|
||||||
|
// unluckily webrtc-rs does not use the same port to do the STUN request
|
||||||
|
static DEFAULT_ICE_SERVERS: [&str; 3] = [
|
||||||
|
"stun:stun.cloudflare.com:3478",
|
||||||
|
"stun:stun.nextcloud.com:3478",
|
||||||
|
"stun:stun.nextcloud.com:443",
|
||||||
|
];
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref SESSIONS: Arc::<Mutex<HashMap<String, WebRTCStream>>> = Default::default();
|
static ref SESSIONS: Arc::<Mutex<HashMap<String, WebRTCStream>>> = Default::default();
|
||||||
@@ -126,7 +137,35 @@ impl WebRTCStream {
|
|||||||
Self::get_key_for_sdp(&desc)
|
Self::get_key_for_sdp(&desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new(remote_endpoint: &str, ms_timeout: u64) -> ResultType<Self> {
|
#[inline]
|
||||||
|
fn get_turn_server_from_url(url: &str) -> Option<RTCIceServer> {
|
||||||
|
// standard url format with turn scheme: turn://user:pass@host:port
|
||||||
|
match Url::parse(url) {
|
||||||
|
Ok(u) => {
|
||||||
|
if u.scheme() == "turn" {
|
||||||
|
Some(RTCIceServer {
|
||||||
|
urls: vec![format!(
|
||||||
|
"turn:{}:{}",
|
||||||
|
u.host_str().unwrap_or_default(),
|
||||||
|
u.port().unwrap_or(3478)
|
||||||
|
)],
|
||||||
|
username: u.username().to_string(),
|
||||||
|
credential: u.password().unwrap_or_default().to_string(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new(
|
||||||
|
remote_endpoint: &str,
|
||||||
|
force_relay: bool,
|
||||||
|
ms_timeout: u64,
|
||||||
|
) -> ResultType<Self> {
|
||||||
log::debug!("New webrtc stream to endpoint: {}", remote_endpoint);
|
log::debug!("New webrtc stream to endpoint: {}", remote_endpoint);
|
||||||
let remote_offer = if remote_endpoint.is_empty() {
|
let remote_offer = if remote_endpoint.is_empty() {
|
||||||
"".into()
|
"".into()
|
||||||
@@ -144,6 +183,7 @@ impl WebRTCStream {
|
|||||||
}
|
}
|
||||||
drop(sessions_lock);
|
drop(sessions_lock);
|
||||||
|
|
||||||
|
let start_local_offer = remote_offer.is_empty();
|
||||||
// Create a SettingEngine and enable Detach
|
// Create a SettingEngine and enable Detach
|
||||||
let mut s = SettingEngine::default();
|
let mut s = SettingEngine::default();
|
||||||
s.detach_data_channels();
|
s.detach_data_channels();
|
||||||
@@ -151,17 +191,29 @@ impl WebRTCStream {
|
|||||||
|
|
||||||
// Create the API object
|
// Create the API object
|
||||||
let api = APIBuilder::new().with_setting_engine(s).build();
|
let api = APIBuilder::new().with_setting_engine(s).build();
|
||||||
|
let mut ice_servers = vec![RTCIceServer {
|
||||||
|
urls: DEFAULT_ICE_SERVERS.iter().map(|s| s.to_string()).collect(),
|
||||||
|
..Default::default()
|
||||||
|
}];
|
||||||
|
if start_local_offer {
|
||||||
|
// only offer needs TURN server
|
||||||
|
let relay_server = config::Config::get_option(config::keys::OPTION_RELAY_SERVER);
|
||||||
|
if let Some(turn_server) = Self::get_turn_server_from_url(&relay_server) {
|
||||||
|
ice_servers.push(turn_server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the configuration
|
// Prepare the configuration
|
||||||
let config = RTCConfiguration {
|
let config = RTCConfiguration {
|
||||||
ice_servers: vec![RTCIceServer {
|
ice_servers,
|
||||||
urls: vec![DEFAULT_ICE_SERVER.to_string()],
|
ice_transport_policy: if force_relay {
|
||||||
..Default::default()
|
RTCIceTransportPolicy::Relay
|
||||||
}],
|
} else {
|
||||||
|
RTCIceTransportPolicy::All
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let start_local_offer = remote_offer.is_empty();
|
|
||||||
let (notify_tx, notify_rx) = watch::channel(false);
|
let (notify_tx, notify_rx) = watch::channel(false);
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
let pc = Arc::new(api.new_peer_connection(config).await?);
|
let pc = Arc::new(api.new_peer_connection(config).await?);
|
||||||
@@ -409,6 +461,54 @@ mod tests {
|
|||||||
use crate::webrtc::WebRTCStream;
|
use crate::webrtc::WebRTCStream;
|
||||||
use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;
|
use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_webrtc_turn_url() {
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("turn://example.com:3478")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.urls[0],
|
||||||
|
"turn:example.com:3478"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("turn://example.com")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.urls[0],
|
||||||
|
"turn:example.com:3478"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("turn://123@example.com")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.username,
|
||||||
|
"123"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("turn://123@example.com")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.credential,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("turn://123:321@example.com")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.credential,
|
||||||
|
"321"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("stun://example.com:3478"),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
WebRTCStream::get_turn_server_from_url("http://123:123@example.com:3478"),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_webrtc_session_key() {
|
fn test_webrtc_session_key() {
|
||||||
let mut sdp_str = "".to_owned();
|
let mut sdp_str = "".to_owned();
|
||||||
@@ -554,18 +654,18 @@ IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDY0MDA4XHJcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNc
|
|||||||
async fn test_webrtc_new_stream() {
|
async fn test_webrtc_new_stream() {
|
||||||
let mut endpoint = "webrtc://sdfsdf".to_owned();
|
let mut endpoint = "webrtc://sdfsdf".to_owned();
|
||||||
assert!(
|
assert!(
|
||||||
WebRTCStream::new(&endpoint, 10000).await.is_err(),
|
WebRTCStream::new(&endpoint, false, 10000).await.is_err(),
|
||||||
"invalid webrtc endpoint should error"
|
"invalid webrtc endpoint should error"
|
||||||
);
|
);
|
||||||
|
|
||||||
endpoint = "wss://sdfsdf".to_owned();
|
endpoint = "wss://sdfsdf".to_owned();
|
||||||
assert!(
|
assert!(
|
||||||
WebRTCStream::new(&endpoint, 10000).await.is_err(),
|
WebRTCStream::new(&endpoint, false, 10000).await.is_err(),
|
||||||
"invalid webrtc endpoint should error"
|
"invalid webrtc endpoint should error"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
WebRTCStream::new("", 10000).await.is_ok(),
|
WebRTCStream::new("", false, 10000).await.is_ok(),
|
||||||
"local webrtc endpoint should ok"
|
"local webrtc endpoint should ok"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -580,7 +680,7 @@ LjIgNjQwMDcgdHlwIGhvc3RcclxuYT1jYW5kaWRhdGU6MTg2MTA0NTE5MCAxIHVkcCAxNjk0NDk4ODE1
|
|||||||
ggcmFkZHIgMC4wLjAuMCBycG9ydCA2NDAwOFxyXG5hPWNhbmRpZGF0ZToxODYxMDQ1MTkwIDIgdWRwIDE2OTQ0OTg4MTUgMTQuMjEyLjY4LjEyIDI3MDA0\
|
ggcmFkZHIgMC4wLjAuMCBycG9ydCA2NDAwOFxyXG5hPWNhbmRpZGF0ZToxODYxMDQ1MTkwIDIgdWRwIDE2OTQ0OTg4MTUgMTQuMjEyLjY4LjEyIDI3MDA0\
|
||||||
IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDY0MDA4XHJcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNcclxuIn0=".to_owned();
|
IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDY0MDA4XHJcbmE9ZW5kLW9mLWNhbmRpZGF0ZXNcclxuIn0=".to_owned();
|
||||||
assert!(
|
assert!(
|
||||||
WebRTCStream::new(&endpoint, 10000).await.is_err(),
|
WebRTCStream::new(&endpoint, false, 10000).await.is_err(),
|
||||||
"connect to an 'answer' webrtc endpoint should error"
|
"connect to an 'answer' webrtc endpoint should error"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user