mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-07-11 09:45:11 +03:00
feat: route WebRTC ICE on controlled side
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
@@ -21,8 +22,13 @@ use hbb_common::{
|
|||||||
rendezvous_proto::*,
|
rendezvous_proto::*,
|
||||||
sleep,
|
sleep,
|
||||||
socket_client::{self, connect_tcp, is_ipv4, new_direct_udp_for, new_udp_for},
|
socket_client::{self, connect_tcp, is_ipv4, new_direct_udp_for, new_udp_for},
|
||||||
tokio::{self, select, sync::Mutex, time::interval},
|
tokio::{
|
||||||
|
self, select,
|
||||||
|
sync::{mpsc, Mutex},
|
||||||
|
time::interval,
|
||||||
|
},
|
||||||
udp::FramedSocket,
|
udp::FramedSocket,
|
||||||
|
webrtc::WebRTCStream,
|
||||||
AddrMangle, IntoTargetAddr, ResultType, Stream, TargetAddr,
|
AddrMangle, IntoTargetAddr, ResultType, Stream, TargetAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,11 +38,17 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Message = RendezvousMessage;
|
type Message = RendezvousMessage;
|
||||||
|
type RendezvousSender = mpsc::UnboundedSender<Message>;
|
||||||
|
|
||||||
|
fn webrtc_ice_key(peer_id: &str, session_key: &str) -> String {
|
||||||
|
format!("{}\n{}", peer_id, session_key)
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref SOLVING_PK_MISMATCH: Mutex<String> = Default::default();
|
static ref SOLVING_PK_MISMATCH: Mutex<String> = Default::default();
|
||||||
static ref LAST_MSG: Mutex<(SocketAddr, Instant)> = Mutex::new((SocketAddr::new([0; 4].into(), 0), Instant::now()));
|
static ref LAST_MSG: Mutex<(SocketAddr, Instant)> = Mutex::new((SocketAddr::new([0; 4].into(), 0), Instant::now()));
|
||||||
static ref LAST_RELAY_MSG: Mutex<(SocketAddr, Instant)> = Mutex::new((SocketAddr::new([0; 4].into(), 0), Instant::now()));
|
static ref LAST_RELAY_MSG: Mutex<(SocketAddr, Instant)> = Mutex::new((SocketAddr::new([0; 4].into(), 0), Instant::now()));
|
||||||
|
static ref WEBRTC_ICE_TXS: Mutex<HashMap<String, mpsc::UnboundedSender<String>>> = Default::default();
|
||||||
}
|
}
|
||||||
static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
|
static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
|
||||||
static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false);
|
static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false);
|
||||||
@@ -72,6 +84,7 @@ pub struct RendezvousMediator {
|
|||||||
host: String,
|
host: String,
|
||||||
host_prefix: String,
|
host_prefix: String,
|
||||||
keep_alive: i32,
|
keep_alive: i32,
|
||||||
|
rz_sender: RendezvousSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RendezvousMediator {
|
impl RendezvousMediator {
|
||||||
@@ -182,11 +195,13 @@ impl RendezvousMediator {
|
|||||||
let host = check_port(&host, RENDEZVOUS_PORT);
|
let host = check_port(&host, RENDEZVOUS_PORT);
|
||||||
log::info!("start udp: {host}");
|
log::info!("start udp: {host}");
|
||||||
let (mut socket, mut addr) = new_udp_for(&host, CONNECT_TIMEOUT).await?;
|
let (mut socket, mut addr) = new_udp_for(&host, CONNECT_TIMEOUT).await?;
|
||||||
|
let (rz_sender, mut rz_out_rx) = mpsc::unbounded_channel::<Message>();
|
||||||
let mut rz = Self {
|
let mut rz = Self {
|
||||||
addr: addr.clone(),
|
addr: addr.clone(),
|
||||||
host: host.clone(),
|
host: host.clone(),
|
||||||
host_prefix: Self::get_host_prefix(&host),
|
host_prefix: Self::get_host_prefix(&host),
|
||||||
keep_alive: crate::DEFAULT_KEEP_ALIVE,
|
keep_alive: crate::DEFAULT_KEEP_ALIVE,
|
||||||
|
rz_sender,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT));
|
let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT));
|
||||||
@@ -246,6 +261,9 @@ impl RendezvousMediator {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Some(msg_out) = rz_out_rx.recv() => {
|
||||||
|
Sink::Framed(&mut socket, &addr).send(&msg_out).await?;
|
||||||
|
},
|
||||||
_ = timer.tick() => {
|
_ = timer.tick() => {
|
||||||
if SHOULD_EXIT.load(Ordering::SeqCst) {
|
if SHOULD_EXIT.load(Ordering::SeqCst) {
|
||||||
break;
|
break;
|
||||||
@@ -367,6 +385,22 @@ impl RendezvousMediator {
|
|||||||
allow_err!(rz.handle_intranet(fla, server).await);
|
allow_err!(rz.handle_intranet(fla, server).await);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Some(rendezvous_message::Union::IceCandidate(ice)) => {
|
||||||
|
if ice.to_id != Config::get_id() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let key = webrtc_ice_key(&ice.from_id, &ice.session_key);
|
||||||
|
let tx = WEBRTC_ICE_TXS.lock().await.get(&key).cloned();
|
||||||
|
if let Some(tx) = tx {
|
||||||
|
let _ = tx.send(ice.candidate);
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"dropping ICE candidate for unknown WebRTC session from {} key {}",
|
||||||
|
ice.from_id,
|
||||||
|
ice.session_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(rendezvous_message::Union::ConfigureUpdate(cu)) => {
|
Some(rendezvous_message::Union::ConfigureUpdate(cu)) => {
|
||||||
let v0 = Config::get_rendezvous_servers();
|
let v0 = Config::get_rendezvous_servers();
|
||||||
Config::set_option(
|
Config::set_option(
|
||||||
@@ -389,11 +423,13 @@ impl RendezvousMediator {
|
|||||||
let mut conn = connect_tcp(host.clone(), CONNECT_TIMEOUT).await?;
|
let mut conn = connect_tcp(host.clone(), CONNECT_TIMEOUT).await?;
|
||||||
let key = crate::get_key(true).await;
|
let key = crate::get_key(true).await;
|
||||||
crate::secure_tcp(&mut conn, &key).await?;
|
crate::secure_tcp(&mut conn, &key).await?;
|
||||||
|
let (rz_sender, mut rz_out_rx) = mpsc::unbounded_channel::<Message>();
|
||||||
let mut rz = Self {
|
let mut rz = Self {
|
||||||
addr: conn.local_addr().into_target_addr()?,
|
addr: conn.local_addr().into_target_addr()?,
|
||||||
host: host.clone(),
|
host: host.clone(),
|
||||||
host_prefix: Self::get_host_prefix(&host),
|
host_prefix: Self::get_host_prefix(&host),
|
||||||
keep_alive: crate::DEFAULT_KEEP_ALIVE,
|
keep_alive: crate::DEFAULT_KEEP_ALIVE,
|
||||||
|
rz_sender,
|
||||||
};
|
};
|
||||||
let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT));
|
let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT));
|
||||||
let mut last_register_sent: Option<Instant> = None;
|
let mut last_register_sent: Option<Instant> = None;
|
||||||
@@ -421,6 +457,9 @@ impl RendezvousMediator {
|
|||||||
let msg = Message::parse_from_bytes(&bytes)?;
|
let msg = Message::parse_from_bytes(&bytes)?;
|
||||||
rz.handle_resp(msg.union, Sink::Stream(&mut conn), &server, &mut update_latency).await?
|
rz.handle_resp(msg.union, Sink::Stream(&mut conn), &server, &mut update_latency).await?
|
||||||
}
|
}
|
||||||
|
Some(msg_out) = rz_out_rx.recv() => {
|
||||||
|
Sink::Stream(&mut conn).send(&msg_out).await?;
|
||||||
|
}
|
||||||
_ = timer.tick() => {
|
_ = timer.tick() => {
|
||||||
if SHOULD_EXIT.load(Ordering::SeqCst) {
|
if SHOULD_EXIT.load(Ordering::SeqCst) {
|
||||||
break;
|
break;
|
||||||
@@ -472,6 +511,7 @@ impl RendezvousMediator {
|
|||||||
rr.secure,
|
rr.secure,
|
||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
String::new(),
|
||||||
rr.control_permissions.clone().into_option(),
|
rr.control_permissions.clone().into_option(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -486,6 +526,7 @@ impl RendezvousMediator {
|
|||||||
secure: bool,
|
secure: bool,
|
||||||
initiate: bool,
|
initiate: bool,
|
||||||
socket_addr_v6: bytes::Bytes,
|
socket_addr_v6: bytes::Bytes,
|
||||||
|
webrtc_sdp_answer: String,
|
||||||
control_permissions: Option<ControlPermissions>,
|
control_permissions: Option<ControlPermissions>,
|
||||||
) -> ResultType<()> {
|
) -> ResultType<()> {
|
||||||
let peer_addr = AddrMangle::decode(&socket_addr);
|
let peer_addr = AddrMangle::decode(&socket_addr);
|
||||||
@@ -504,6 +545,7 @@ impl RendezvousMediator {
|
|||||||
socket_addr: socket_addr.into(),
|
socket_addr: socket_addr.into(),
|
||||||
version: crate::VERSION.to_owned(),
|
version: crate::VERSION.to_owned(),
|
||||||
socket_addr_v6,
|
socket_addr_v6,
|
||||||
|
webrtc_sdp_answer,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if initiate {
|
if initiate {
|
||||||
@@ -571,6 +613,7 @@ impl RendezvousMediator {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
socket_addr_v6,
|
socket_addr_v6,
|
||||||
|
String::new(),
|
||||||
fla.control_permissions.into_option(),
|
fla.control_permissions.into_option(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -613,6 +656,91 @@ impl RendezvousMediator {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn spawn_webrtc_answerer(
|
||||||
|
&self,
|
||||||
|
ph: &PunchHole,
|
||||||
|
server: ServerPtr,
|
||||||
|
peer_addr: SocketAddr,
|
||||||
|
control_permissions: Option<ControlPermissions>,
|
||||||
|
) -> ResultType<String> {
|
||||||
|
if ph.requester_id.is_empty() {
|
||||||
|
log::warn!("WebRTC offer missing requester_id; falling back to existing transports");
|
||||||
|
return Ok(String::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stream =
|
||||||
|
WebRTCStream::new(&ph.webrtc_sdp_offer, ph.force_relay, CONNECT_TIMEOUT).await?;
|
||||||
|
let answer = stream.get_local_endpoint().await?;
|
||||||
|
let session_key = stream.session_key().to_owned();
|
||||||
|
let peer_id = ph.requester_id.clone();
|
||||||
|
|
||||||
|
let (remote_ice_tx, mut remote_ice_rx) = mpsc::unbounded_channel::<String>();
|
||||||
|
WEBRTC_ICE_TXS
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.insert(webrtc_ice_key(&peer_id, &session_key), remote_ice_tx);
|
||||||
|
|
||||||
|
let stream_for_remote_ice = stream.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(candidate) = remote_ice_rx.recv().await {
|
||||||
|
if let Err(err) = stream_for_remote_ice.add_remote_ice_candidate(&candidate).await
|
||||||
|
{
|
||||||
|
log::warn!("failed to add remote WebRTC ICE candidate: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(mut local_ice_rx) = stream.take_local_ice_rx() {
|
||||||
|
let sender = self.rz_sender.clone();
|
||||||
|
let my_id = Config::get_id();
|
||||||
|
let target_id = peer_id.clone();
|
||||||
|
let session_key_for_ice = session_key.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(candidate) = local_ice_rx.recv().await {
|
||||||
|
let mut msg = Message::new();
|
||||||
|
msg.set_ice_candidate(IceCandidate {
|
||||||
|
from_id: my_id.clone(),
|
||||||
|
to_id: target_id.clone(),
|
||||||
|
session_key: session_key_for_ice.clone(),
|
||||||
|
candidate,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let _ = sender.send(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let peer_id_for_cleanup = peer_id.clone();
|
||||||
|
let session_key_for_cleanup = session_key.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let result = stream.wait_connected(CONNECT_TIMEOUT).await;
|
||||||
|
WEBRTC_ICE_TXS
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.remove(&webrtc_ice_key(
|
||||||
|
&peer_id_for_cleanup,
|
||||||
|
&session_key_for_cleanup,
|
||||||
|
));
|
||||||
|
if let Err(err) = result {
|
||||||
|
log::warn!("webrtc wait_connected failed: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(err) = crate::server::create_tcp_connection(
|
||||||
|
server,
|
||||||
|
Stream::WebRTC(stream),
|
||||||
|
peer_addr,
|
||||||
|
true,
|
||||||
|
control_permissions,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::warn!("failed to create WebRTC server connection: {}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(answer)
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_punch_hole(&self, ph: PunchHole, server: ServerPtr) -> ResultType<()> {
|
async fn handle_punch_hole(&self, ph: PunchHole, server: ServerPtr) -> ResultType<()> {
|
||||||
let mut peer_addr = AddrMangle::decode(&ph.socket_addr);
|
let mut peer_addr = AddrMangle::decode(&ph.socket_addr);
|
||||||
let last = *LAST_MSG.lock().await;
|
let last = *LAST_MSG.lock().await;
|
||||||
@@ -624,7 +752,22 @@ impl RendezvousMediator {
|
|||||||
let peer_addr_v6 = hbb_common::AddrMangle::decode(&ph.socket_addr_v6);
|
let peer_addr_v6 = hbb_common::AddrMangle::decode(&ph.socket_addr_v6);
|
||||||
let relay = use_ws() || Config::is_proxy() || ph.force_relay;
|
let relay = use_ws() || Config::is_proxy() || ph.force_relay;
|
||||||
let mut socket_addr_v6 = Default::default();
|
let mut socket_addr_v6 = Default::default();
|
||||||
let control_permissions = ph.control_permissions.into_option();
|
let control_permissions = ph.control_permissions.clone().into_option();
|
||||||
|
let webrtc_sdp_answer = if !ph.webrtc_sdp_offer.is_empty() {
|
||||||
|
self.spawn_webrtc_answerer(
|
||||||
|
&ph,
|
||||||
|
server.clone(),
|
||||||
|
peer_addr,
|
||||||
|
control_permissions.clone(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
log::warn!("failed to create WebRTC answer: {}", err);
|
||||||
|
String::new()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
if peer_addr_v6.port() > 0 && !relay {
|
if peer_addr_v6.port() > 0 && !relay {
|
||||||
socket_addr_v6 = start_ipv6(
|
socket_addr_v6 = start_ipv6(
|
||||||
peer_addr_v6,
|
peer_addr_v6,
|
||||||
@@ -651,6 +794,7 @@ impl RendezvousMediator {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
socket_addr_v6.clone(),
|
socket_addr_v6.clone(),
|
||||||
|
webrtc_sdp_answer.clone(),
|
||||||
control_permissions,
|
control_permissions,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -664,6 +808,7 @@ impl RendezvousMediator {
|
|||||||
nat_type: nat_type.into(),
|
nat_type: nat_type.into(),
|
||||||
version: crate::VERSION.to_owned(),
|
version: crate::VERSION.to_owned(),
|
||||||
socket_addr_v6,
|
socket_addr_v6,
|
||||||
|
webrtc_sdp_answer,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if ph.udp_port > 0 {
|
if ph.udp_port > 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user