mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-17 19:44:49 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1040df0399 | ||
|
|
03cbf609f6 | ||
|
|
0e0ec5a551 | ||
|
|
c50e7d078d | ||
|
|
3d6b06e854 | ||
|
|
472c4fc03a | ||
|
|
9f8f726f12 | ||
|
|
701a9c6cdc | ||
|
|
0d40cf2101 |
@@ -47,7 +47,7 @@ screencapturekit = ["cpal/screencapturekit"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
scrap = { path = "libs/scrap", features = ["wayland"] }
|
scrap = { path = "libs/scrap", features = ["wayland"] }
|
||||||
hbb_common = { path = "libs/hbb_common" }
|
hbb_common = { path = "libs/hbb_common", features = ["webrtc"] }
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|||||||
Submodule libs/hbb_common updated: c8cbb6be28...c7f5567865
21
src/cli.rs
21
src/cli.rs
@@ -3,7 +3,7 @@ use async_trait::async_trait;
|
|||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::PeerConfig,
|
config::PeerConfig,
|
||||||
config::READ_TIMEOUT,
|
config::READ_TIMEOUT,
|
||||||
futures::{SinkExt, StreamExt},
|
futures::StreamExt,
|
||||||
log,
|
log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
@@ -46,6 +46,7 @@ impl Session {
|
|||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
session
|
session
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ impl Session {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Interface for Session {
|
impl Interface for Session {
|
||||||
fn get_login_config_handler(&self) -> Arc<RwLock<LoginConfigHandler>> {
|
fn get_lch(&self) -> Arc<RwLock<LoginConfigHandler>> {
|
||||||
return self.lc.clone();
|
return self.lc.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +62,20 @@ impl Interface for Session {
|
|||||||
match msgtype {
|
match msgtype {
|
||||||
"input-password" => {
|
"input-password" => {
|
||||||
self.sender
|
self.sender
|
||||||
.send(Data::Login((self.password.clone(), true)))
|
.send(Data::Login((
|
||||||
|
String::new(),
|
||||||
|
String::new(),
|
||||||
|
self.password.clone(),
|
||||||
|
true,
|
||||||
|
)))
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
"re-input-password" => {
|
"re-input-password" => {
|
||||||
log::error!("{}: {}", title, text);
|
log::error!("{}: {}", title, text);
|
||||||
match rpassword::prompt_password("Enter password: ") {
|
match rpassword::prompt_password("Enter password: ") {
|
||||||
Ok(password) => {
|
Ok(password) => {
|
||||||
let login_data = Data::Login((password, true));
|
let login_data =
|
||||||
|
Data::Login((String::new(), String::new(), password, true));
|
||||||
self.sender.send(login_data).ok();
|
self.sender.send(login_data).ok();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -93,6 +100,8 @@ impl Interface for Session {
|
|||||||
self.lc.write().unwrap().handle_peer_info(&pi);
|
self.lc.write().unwrap().handle_peer_info(&pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_multiple_windows_session(&self, _sessions: Vec<WindowsSession>) {}
|
||||||
|
|
||||||
async fn handle_hash(&self, pass: &str, hash: Hash, peer: &mut Stream) {
|
async fn handle_hash(&self, pass: &str, hash: Hash, peer: &mut Stream) {
|
||||||
log::info!(
|
log::info!(
|
||||||
"password={}",
|
"password={}",
|
||||||
@@ -137,8 +146,8 @@ pub async fn connect_test(id: &str, key: String, token: String) {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("Failed to connect {}: {}", &id, err);
|
log::error!("Failed to connect {}: {}", &id, err);
|
||||||
}
|
}
|
||||||
Ok((mut stream, direct)) => {
|
Ok(((mut stream, _direct, _secure, _kcp, _typ), direct)) => {
|
||||||
log::info!("direct: {}", direct);
|
log::info!("direct: {:?}", direct);
|
||||||
// rpassword::prompt_password("Input anything to exit").ok();
|
// rpassword::prompt_password("Input anything to exit").ok();
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
|||||||
218
src/client.rs
218
src/client.rs
@@ -65,11 +65,12 @@ use hbb_common::{
|
|||||||
self,
|
self,
|
||||||
net::UdpSocket,
|
net::UdpSocket,
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{unbounded_channel, UnboundedReceiver},
|
mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver},
|
||||||
oneshot,
|
oneshot,
|
||||||
},
|
},
|
||||||
time::{interval, Duration, Instant},
|
time::{interval, Duration, Instant},
|
||||||
},
|
},
|
||||||
|
webrtc::WebRTCStream,
|
||||||
AddrMangle, ResultType, Stream,
|
AddrMangle, ResultType, Stream,
|
||||||
};
|
};
|
||||||
pub use helper::*;
|
pub use helper::*;
|
||||||
@@ -330,6 +331,19 @@ impl Client {
|
|||||||
} else {
|
} else {
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
let ipv6 = if crate::get_ipv6_punch_enabled() {
|
||||||
|
crate::get_ipv6_socket().await
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let webrtc_offerer =
|
||||||
|
match WebRTCStream::new("", interface.is_force_relay(), CONNECT_TIMEOUT).await {
|
||||||
|
Ok(stream) => Some(stream),
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("webrtc offerer setup failed: {}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
let fut = Self::_start_inner(
|
let fut = Self::_start_inner(
|
||||||
peer.to_owned(),
|
peer.to_owned(),
|
||||||
key.to_owned(),
|
key.to_owned(),
|
||||||
@@ -338,6 +352,8 @@ impl Client {
|
|||||||
interface.clone(),
|
interface.clone(),
|
||||||
udp.clone(),
|
udp.clone(),
|
||||||
Some(stop_udp_tx),
|
Some(stop_udp_tx),
|
||||||
|
ipv6,
|
||||||
|
webrtc_offerer,
|
||||||
rendezvous_server.clone(),
|
rendezvous_server.clone(),
|
||||||
servers.clone(),
|
servers.clone(),
|
||||||
contained,
|
contained,
|
||||||
@@ -355,6 +371,8 @@ impl Client {
|
|||||||
interface,
|
interface,
|
||||||
(None, None),
|
(None, None),
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
rendezvous_server,
|
rendezvous_server,
|
||||||
servers,
|
servers,
|
||||||
contained,
|
contained,
|
||||||
@@ -366,6 +384,68 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_webrtc_ice_bridge(
|
||||||
|
mut socket: Stream,
|
||||||
|
mut local_ice_rx: Option<UnboundedReceiver<String>>,
|
||||||
|
webrtc: WebRTCStream,
|
||||||
|
peer: String,
|
||||||
|
session_key: String,
|
||||||
|
) -> oneshot::Sender<()> {
|
||||||
|
let (stop_tx, mut stop_rx) = oneshot::channel::<()>();
|
||||||
|
let my_id = Config::get_id();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
match stop_rx.try_recv() {
|
||||||
|
Ok(_) | Err(tokio::sync::oneshot::error::TryRecvError::Closed) => break,
|
||||||
|
Err(tokio::sync::oneshot::error::TryRecvError::Empty) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rx) = local_ice_rx.as_mut() {
|
||||||
|
loop {
|
||||||
|
match rx.try_recv() {
|
||||||
|
Ok(candidate) => {
|
||||||
|
let mut msg = RendezvousMessage::new();
|
||||||
|
msg.set_ice_candidate(IceCandidate {
|
||||||
|
from_id: my_id.clone(),
|
||||||
|
to_id: peer.clone(),
|
||||||
|
session_key: session_key.clone(),
|
||||||
|
candidate,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
if let Err(err) = socket.send(&msg).await {
|
||||||
|
log::warn!("failed to send WebRTC ICE candidate: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(TryRecvError::Empty) => break,
|
||||||
|
Err(TryRecvError::Disconnected) => {
|
||||||
|
local_ice_rx = None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(msg_in) =
|
||||||
|
crate::get_next_nonkeyexchange_msg(&mut socket, Some(100)).await
|
||||||
|
{
|
||||||
|
if let Some(rendezvous_message::Union::IceCandidate(ice)) = msg_in.union {
|
||||||
|
if ice.from_id == peer
|
||||||
|
&& ice.to_id == my_id
|
||||||
|
&& ice.session_key == session_key
|
||||||
|
{
|
||||||
|
if let Err(err) = webrtc.add_remote_ice_candidate(&ice.candidate).await
|
||||||
|
{
|
||||||
|
log::warn!("failed to add WebRTC ICE candidate: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stop_tx
|
||||||
|
}
|
||||||
|
|
||||||
async fn _start_inner(
|
async fn _start_inner(
|
||||||
peer: String,
|
peer: String,
|
||||||
key: String,
|
key: String,
|
||||||
@@ -374,6 +454,8 @@ impl Client {
|
|||||||
interface: impl Interface,
|
interface: impl Interface,
|
||||||
mut udp: (Option<Arc<UdpSocket>>, Option<Arc<Mutex<u16>>>),
|
mut udp: (Option<Arc<UdpSocket>>, Option<Arc<Mutex<u16>>>),
|
||||||
stop_udp_tx: Option<oneshot::Sender<()>>,
|
stop_udp_tx: Option<oneshot::Sender<()>>,
|
||||||
|
mut ipv6: Option<(Arc<UdpSocket>, bytes::Bytes)>,
|
||||||
|
mut webrtc_offerer: Option<WebRTCStream>,
|
||||||
mut rendezvous_server: String,
|
mut rendezvous_server: String,
|
||||||
servers: Vec<String>,
|
servers: Vec<String>,
|
||||||
contained: bool,
|
contained: bool,
|
||||||
@@ -446,14 +528,20 @@ impl Client {
|
|||||||
// Stop UDP NAT test task if still running
|
// Stop UDP NAT test task if still running
|
||||||
stop_udp_tx.map(|tx| tx.send(()));
|
stop_udp_tx.map(|tx| tx.send(()));
|
||||||
let mut msg_out = RendezvousMessage::new();
|
let mut msg_out = RendezvousMessage::new();
|
||||||
let mut ipv6 = if crate::get_ipv6_punch_enabled() {
|
let mut ipv6 = ipv6
|
||||||
if let Some((socket, addr)) = crate::get_ipv6_socket().await {
|
.take()
|
||||||
(Some(socket), Some(addr))
|
.map(|(socket, addr)| (Some(socket), Some(addr)))
|
||||||
} else {
|
.unwrap_or((None, None));
|
||||||
(None, None)
|
let webrtc_sdp_offer = if let Some(webrtc) = webrtc_offerer.as_ref() {
|
||||||
|
match webrtc.get_local_endpoint().await {
|
||||||
|
Ok(endpoint) => endpoint,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to read local WebRTC offer: {}", err);
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
String::new()
|
||||||
};
|
};
|
||||||
let udp_nat_port = udp.1.map(|x| *x.lock().unwrap()).unwrap_or(0);
|
let udp_nat_port = udp.1.map(|x| *x.lock().unwrap()).unwrap_or(0);
|
||||||
let punch_type = if udp_nat_port > 0 { "UDP" } else { "TCP" };
|
let punch_type = if udp_nat_port > 0 { "UDP" } else { "TCP" };
|
||||||
@@ -467,8 +555,15 @@ impl Client {
|
|||||||
udp_port: udp_nat_port as _,
|
udp_port: udp_nat_port as _,
|
||||||
force_relay: interface.is_force_relay(),
|
force_relay: interface.is_force_relay(),
|
||||||
socket_addr_v6: ipv6.1.unwrap_or_default(),
|
socket_addr_v6: ipv6.1.unwrap_or_default(),
|
||||||
|
webrtc_sdp_offer: webrtc_sdp_offer.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
let webrtc_session_key = webrtc_offerer
|
||||||
|
.as_ref()
|
||||||
|
.map(|webrtc| webrtc.session_key().to_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let mut webrtc_sdp_answer = String::new();
|
||||||
|
let mut pending_webrtc_ice = Vec::<String>::new();
|
||||||
for i in 1..=3 {
|
for i in 1..=3 {
|
||||||
log::info!(
|
log::info!(
|
||||||
"#{} {} punch attempt with {}, id: {}",
|
"#{} {} punch attempt with {}, id: {}",
|
||||||
@@ -510,6 +605,7 @@ impl Client {
|
|||||||
relay_server = ph.relay_server;
|
relay_server = ph.relay_server;
|
||||||
peer_addr = AddrMangle::decode(&ph.socket_addr);
|
peer_addr = AddrMangle::decode(&ph.socket_addr);
|
||||||
feedback = ph.feedback;
|
feedback = ph.feedback;
|
||||||
|
webrtc_sdp_answer = ph.webrtc_sdp_answer;
|
||||||
let s = udp.0.take();
|
let s = udp.0.take();
|
||||||
if ph.is_udp && s.is_some() {
|
if ph.is_udp && s.is_some() {
|
||||||
if let Some(s) = s {
|
if let Some(s) = s {
|
||||||
@@ -549,6 +645,38 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
signed_id_pk = rr.pk().into();
|
signed_id_pk = rr.pk().into();
|
||||||
|
let mut webrtc_bridge_stop = None;
|
||||||
|
let mut webrtc_for_connect = None;
|
||||||
|
if !rr.webrtc_sdp_answer.is_empty() {
|
||||||
|
if let Some(webrtc) = webrtc_offerer.take() {
|
||||||
|
if let Err(err) =
|
||||||
|
webrtc.set_remote_endpoint(&rr.webrtc_sdp_answer).await
|
||||||
|
{
|
||||||
|
log::warn!("failed to set WebRTC relay answer: {}", err);
|
||||||
|
} else {
|
||||||
|
for candidate in pending_webrtc_ice.drain(..) {
|
||||||
|
if let Err(err) =
|
||||||
|
webrtc.add_remote_ice_candidate(&candidate).await
|
||||||
|
{
|
||||||
|
log::warn!(
|
||||||
|
"failed to add buffered WebRTC ICE candidate: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let session_key = webrtc.session_key().to_owned();
|
||||||
|
let local_ice_rx = webrtc.take_local_ice_rx();
|
||||||
|
webrtc_bridge_stop = Some(Self::spawn_webrtc_ice_bridge(
|
||||||
|
socket,
|
||||||
|
local_ice_rx,
|
||||||
|
webrtc.clone(),
|
||||||
|
peer.clone(),
|
||||||
|
session_key,
|
||||||
|
));
|
||||||
|
webrtc_for_connect = Some(webrtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let fut = Self::create_relay(
|
let fut = Self::create_relay(
|
||||||
&peer,
|
&peer,
|
||||||
rr.uuid,
|
rr.uuid,
|
||||||
@@ -564,30 +692,86 @@ impl Client {
|
|||||||
}
|
}
|
||||||
.boxed(),
|
.boxed(),
|
||||||
);
|
);
|
||||||
|
if let Some(mut webrtc) = webrtc_for_connect {
|
||||||
|
connect_futures.push(
|
||||||
|
async move {
|
||||||
|
webrtc.wait_connected(CONNECT_TIMEOUT).await?;
|
||||||
|
Ok((Stream::WebRTC(webrtc), None, "WebRTC"))
|
||||||
|
}
|
||||||
|
.boxed(),
|
||||||
|
);
|
||||||
|
}
|
||||||
// Run all connection attempts concurrently, return the first successful one
|
// Run all connection attempts concurrently, return the first successful one
|
||||||
let (conn, kcp, typ) = match select_ok(connect_futures).await {
|
let (conn, kcp, typ) = match select_ok(connect_futures).await {
|
||||||
Ok(conn) => (Ok(conn.0 .0), conn.0 .1, conn.0 .2),
|
Ok(conn) => (Ok(conn.0 .0), conn.0 .1, conn.0 .2),
|
||||||
|
|
||||||
Err(e) => (Err(e), None, ""),
|
Err(e) => (Err(e), None, ""),
|
||||||
};
|
};
|
||||||
|
if let Some(stop) = webrtc_bridge_stop {
|
||||||
|
let _ = stop.send(());
|
||||||
|
}
|
||||||
let mut conn = conn?;
|
let mut conn = conn?;
|
||||||
feedback = rr.feedback;
|
feedback = rr.feedback;
|
||||||
log::info!("{:?} used to establish {typ} connection", start.elapsed());
|
log::info!("{:?} used to establish {typ} connection", start.elapsed());
|
||||||
let pk =
|
let pk =
|
||||||
Self::secure_connection(&peer, signed_id_pk, &key, &mut conn).await?;
|
Self::secure_connection(&peer, signed_id_pk, &key, &mut conn).await?;
|
||||||
return Ok((
|
return Ok((
|
||||||
(conn, typ == "IPv6", pk, kcp, typ),
|
(conn, typ == "IPv6" || typ == "WebRTC", pk, kcp, typ),
|
||||||
(feedback, rendezvous_server),
|
(feedback, rendezvous_server),
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Some(rendezvous_message::Union::IceCandidate(ice)) => {
|
||||||
|
if !webrtc_session_key.is_empty()
|
||||||
|
&& ice.from_id == peer
|
||||||
|
&& ice.to_id == Config::get_id()
|
||||||
|
&& ice.session_key == webrtc_session_key
|
||||||
|
{
|
||||||
|
pending_webrtc_ice.push(ice.candidate);
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"dropping ICE candidate for unexpected WebRTC session from {} key {}",
|
||||||
|
ice.from_id,
|
||||||
|
ice.session_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unexpected protobuf msg received: {:?}", msg_in);
|
log::error!("Unexpected protobuf msg received: {:?}", msg_in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop(socket);
|
let mut webrtc_bridge_stop = None;
|
||||||
|
let mut webrtc_for_connect = None;
|
||||||
|
if !webrtc_sdp_answer.is_empty() {
|
||||||
|
if let Some(webrtc) = webrtc_offerer.take() {
|
||||||
|
if let Err(err) = webrtc.set_remote_endpoint(&webrtc_sdp_answer).await {
|
||||||
|
log::warn!("failed to set WebRTC answer: {}", err);
|
||||||
|
drop(socket);
|
||||||
|
} else {
|
||||||
|
for candidate in pending_webrtc_ice.drain(..) {
|
||||||
|
if let Err(err) = webrtc.add_remote_ice_candidate(&candidate).await {
|
||||||
|
log::warn!("failed to add buffered WebRTC ICE candidate: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let session_key = webrtc.session_key().to_owned();
|
||||||
|
let local_ice_rx = webrtc.take_local_ice_rx();
|
||||||
|
webrtc_bridge_stop = Some(Self::spawn_webrtc_ice_bridge(
|
||||||
|
socket,
|
||||||
|
local_ice_rx,
|
||||||
|
webrtc.clone(),
|
||||||
|
peer.clone(),
|
||||||
|
session_key,
|
||||||
|
));
|
||||||
|
webrtc_for_connect = Some(webrtc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drop(socket);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drop(socket);
|
||||||
|
}
|
||||||
if peer_addr.port() == 0 {
|
if peer_addr.port() == 0 {
|
||||||
bail!("Failed to connect via rendezvous server");
|
bail!("Failed to connect via rendezvous server");
|
||||||
}
|
}
|
||||||
@@ -621,6 +805,8 @@ impl Client {
|
|||||||
interface,
|
interface,
|
||||||
udp.0,
|
udp.0,
|
||||||
ipv6.0,
|
ipv6.0,
|
||||||
|
webrtc_for_connect,
|
||||||
|
webrtc_bridge_stop,
|
||||||
punch_type,
|
punch_type,
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
@@ -647,6 +833,8 @@ impl Client {
|
|||||||
interface: impl Interface,
|
interface: impl Interface,
|
||||||
udp_socket_nat: Option<Arc<UdpSocket>>,
|
udp_socket_nat: Option<Arc<UdpSocket>>,
|
||||||
udp_socket_v6: Option<Arc<UdpSocket>>,
|
udp_socket_v6: Option<Arc<UdpSocket>>,
|
||||||
|
webrtc_offerer: Option<WebRTCStream>,
|
||||||
|
webrtc_bridge_stop: Option<oneshot::Sender<()>>,
|
||||||
punch_type: &str,
|
punch_type: &str,
|
||||||
) -> ResultType<(
|
) -> ResultType<(
|
||||||
Stream,
|
Stream,
|
||||||
@@ -705,11 +893,23 @@ impl Client {
|
|||||||
if let Some(udp_socket_v6) = udp_socket_v6 {
|
if let Some(udp_socket_v6) = udp_socket_v6 {
|
||||||
connect_futures.push(udp_nat_connect(udp_socket_v6, "IPv6", connect_timeout).boxed());
|
connect_futures.push(udp_nat_connect(udp_socket_v6, "IPv6", connect_timeout).boxed());
|
||||||
}
|
}
|
||||||
|
if let Some(mut webrtc) = webrtc_offerer {
|
||||||
|
connect_futures.push(
|
||||||
|
async move {
|
||||||
|
webrtc.wait_connected(connect_timeout).await?;
|
||||||
|
Ok((Stream::WebRTC(webrtc), None, "WebRTC"))
|
||||||
|
}
|
||||||
|
.boxed(),
|
||||||
|
);
|
||||||
|
}
|
||||||
// Run all connection attempts concurrently, return the first successful one
|
// Run all connection attempts concurrently, return the first successful one
|
||||||
let (mut conn, kcp, mut typ) = match select_ok(connect_futures).await {
|
let (mut conn, kcp, mut typ) = match select_ok(connect_futures).await {
|
||||||
Ok(conn) => (Ok(conn.0 .0), conn.0 .1, conn.0 .2),
|
Ok(conn) => (Ok(conn.0 .0), conn.0 .1, conn.0 .2),
|
||||||
Err(e) => (Err(e), None, ""),
|
Err(e) => (Err(e), None, ""),
|
||||||
};
|
};
|
||||||
|
if let Some(stop) = webrtc_bridge_stop {
|
||||||
|
let _ = stop.send(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut direct = !conn.is_err();
|
let mut direct = !conn.is_err();
|
||||||
if interface.is_force_relay() || conn.is_err() {
|
if interface.is_force_relay() || conn.is_err() {
|
||||||
|
|||||||
@@ -627,6 +627,98 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
println!("Installation and administrative privileges required!");
|
println!("Installation and administrative privileges required!");
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
|
} else if args[0] == "--deploy" {
|
||||||
|
if config::Config::no_register_device() {
|
||||||
|
println!("Cannot deploy an unregistrable device!");
|
||||||
|
} else if crate::platform::is_installed() && is_root() {
|
||||||
|
let max = args.len() - 1;
|
||||||
|
let pos = args.iter().position(|x| x == "--token").unwrap_or(max);
|
||||||
|
if pos >= max {
|
||||||
|
println!("--token is required!");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let token = args[pos + 1].to_owned();
|
||||||
|
let get_value = |c: &str| {
|
||||||
|
let pos = args.iter().position(|x| x == c).unwrap_or(max);
|
||||||
|
if pos < max {
|
||||||
|
Some(args[pos + 1].to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let new_id = get_value("--id");
|
||||||
|
let local_id = crate::ipc::get_id();
|
||||||
|
let id_to_deploy = new_id.clone().unwrap_or_else(|| local_id.clone());
|
||||||
|
let uuid = crate::encode64(hbb_common::get_uuid());
|
||||||
|
let pk = crate::encode64(
|
||||||
|
hbb_common::config::Config::get_key_pair().1,
|
||||||
|
);
|
||||||
|
let body = serde_json::json!({
|
||||||
|
"id": id_to_deploy,
|
||||||
|
"uuid": uuid,
|
||||||
|
"pk": pk,
|
||||||
|
});
|
||||||
|
let header = "Authorization: Bearer ".to_owned() + &token;
|
||||||
|
let url = crate::ui_interface::get_api_server() + "/api/devices/deploy";
|
||||||
|
match crate::post_request_sync(url, body.to_string(), &header) {
|
||||||
|
Err(err) => {
|
||||||
|
println!("Request failed: {}", err);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
Ok(text) => {
|
||||||
|
let parsed: serde_json::Value =
|
||||||
|
serde_json::from_str(&text).unwrap_or(serde_json::Value::Null);
|
||||||
|
let result = parsed["result"].as_str().unwrap_or("");
|
||||||
|
match result {
|
||||||
|
"OK" => {
|
||||||
|
if let Some(ref new_id) = new_id {
|
||||||
|
if *new_id != local_id {
|
||||||
|
if let Err(err) =
|
||||||
|
crate::ipc::set_config("id", new_id.clone())
|
||||||
|
{
|
||||||
|
println!(
|
||||||
|
"Failed to persist deployed id locally: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(err) = crate::ipc::notify_deployed() {
|
||||||
|
log::warn!("Failed to notify deployed state: {}", err);
|
||||||
|
}
|
||||||
|
println!("Device deployed.");
|
||||||
|
}
|
||||||
|
"NOT_ENABLED" => {
|
||||||
|
println!("Server does not require deployment.");
|
||||||
|
std::process::exit(3);
|
||||||
|
}
|
||||||
|
"INVALID_INPUT" => {
|
||||||
|
println!("Invalid input.");
|
||||||
|
std::process::exit(5);
|
||||||
|
}
|
||||||
|
"ID_TAKEN" => {
|
||||||
|
println!(
|
||||||
|
"Id `{}` is already used by another machine on the server.",
|
||||||
|
id_to_deploy
|
||||||
|
);
|
||||||
|
std::process::exit(6);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if text.is_empty() {
|
||||||
|
println!("Unknown response.");
|
||||||
|
} else {
|
||||||
|
println!("{}", text);
|
||||||
|
}
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Installation and administrative privileges required!");
|
||||||
|
}
|
||||||
|
return None;
|
||||||
} else if args[0] == "--check-hwcodec-config" {
|
} else if args[0] == "--check-hwcodec-config" {
|
||||||
#[cfg(feature = "hwcodec")]
|
#[cfg(feature = "hwcodec")]
|
||||||
crate::ipc::hwcodec_process();
|
crate::ipc::hwcodec_process();
|
||||||
|
|||||||
12
src/ipc.rs
12
src/ipc.rs
@@ -312,6 +312,7 @@ pub enum Data {
|
|||||||
ClipboardNonFile(Option<(String, Vec<ClipboardNonFile>)>),
|
ClipboardNonFile(Option<(String, Vec<ClipboardNonFile>)>),
|
||||||
PrivacyModeState((i32, PrivacyModeState, String)),
|
PrivacyModeState((i32, PrivacyModeState, String)),
|
||||||
TestRendezvousServer,
|
TestRendezvousServer,
|
||||||
|
Deployed,
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
Keyboard(DataKeyboard),
|
Keyboard(DataKeyboard),
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
@@ -929,6 +930,10 @@ async fn handle(data: Data, stream: &mut Connection) {
|
|||||||
Data::TestRendezvousServer => {
|
Data::TestRendezvousServer => {
|
||||||
crate::test_rendezvous_server();
|
crate::test_rendezvous_server();
|
||||||
}
|
}
|
||||||
|
Data::Deployed => {
|
||||||
|
crate::rendezvous_mediator::NEEDS_DEPLOY.store(false, Ordering::SeqCst);
|
||||||
|
crate::rendezvous_mediator::RendezvousMediator::restart();
|
||||||
|
}
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
Data::SwitchSidesRequest(id) => {
|
Data::SwitchSidesRequest(id) => {
|
||||||
@@ -1737,6 +1742,13 @@ pub async fn test_rendezvous_server() -> ResultType<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
pub async fn notify_deployed() -> ResultType<()> {
|
||||||
|
let mut c = connect(1000, "").await?;
|
||||||
|
c.send(&Data::Deployed).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
pub async fn send_url_scheme(url: String) -> ResultType<()> {
|
pub async fn send_url_scheme(url: String) -> ResultType<()> {
|
||||||
connect(1_000, "_url")
|
connect(1_000, "_url")
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Closed manually by the peer", "Cerrado manualmente por el par"),
|
("Closed manually by the peer", "Cerrado manualmente por el par"),
|
||||||
("Enable remote configuration modification", "Habilitar modificación remota de configuración"),
|
("Enable remote configuration modification", "Habilitar modificación remota de configuración"),
|
||||||
("Run without install", "Ejecutar sin instalar"),
|
("Run without install", "Ejecutar sin instalar"),
|
||||||
("Connect via relay", ""),
|
("Connect via relay", "Conectar a través de relay"),
|
||||||
("Always connect via relay", "Conéctese siempre a través de relay"),
|
("Always connect via relay", "Conéctese siempre a través de relay"),
|
||||||
("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"),
|
("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"),
|
||||||
("Login", "Iniciar sesión"),
|
("Login", "Iniciar sesión"),
|
||||||
@@ -228,7 +228,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Username missed", "Olvidó su nombre de usuario"),
|
("Username missed", "Olvidó su nombre de usuario"),
|
||||||
("Password missed", "Olvidó su contraseña"),
|
("Password missed", "Olvidó su contraseña"),
|
||||||
("Wrong credentials", "Credenciales incorrectas"),
|
("Wrong credentials", "Credenciales incorrectas"),
|
||||||
("The verification code is incorrect or has expired", ""),
|
("The verification code is incorrect or has expired", "El código de verificación es incorrecto o ha caducado"),
|
||||||
("Edit Tag", "Editar tag"),
|
("Edit Tag", "Editar tag"),
|
||||||
("Forget Password", "Olvidar contraseña"),
|
("Forget Password", "Olvidar contraseña"),
|
||||||
("Favorites", "Favoritos"),
|
("Favorites", "Favoritos"),
|
||||||
@@ -302,8 +302,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
||||||
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
||||||
("android_open_battery_optimizations_tip", "Si deseas deshabilitar esta característica, por favor, ve a la página siguiente de ajustes, busca y entra en [Batería] y desmarca [Sin restricción]"),
|
("android_open_battery_optimizations_tip", "Si deseas deshabilitar esta característica, por favor, ve a la página siguiente de ajustes, busca y entra en [Batería] y desmarca [Sin restricción]"),
|
||||||
("Start on boot", ""),
|
("Start on boot", "Iniciar al arrancar"),
|
||||||
("Start the screen sharing service on boot, requires special permissions", ""),
|
("Start the screen sharing service on boot, requires special permissions", "Iniciar el servicio de pantalla compartida al arrancar, requiere permisos especiales"),
|
||||||
("Connection not allowed", "Conexión no disponible"),
|
("Connection not allowed", "Conexión no disponible"),
|
||||||
("Legacy mode", "Modo heredado"),
|
("Legacy mode", "Modo heredado"),
|
||||||
("Map mode", "Modo mapa"),
|
("Map mode", "Modo mapa"),
|
||||||
@@ -326,8 +326,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ratio", "Relación"),
|
("Ratio", "Relación"),
|
||||||
("Image Quality", "Calidad de imagen"),
|
("Image Quality", "Calidad de imagen"),
|
||||||
("Scroll Style", "Estilo de desplazamiento"),
|
("Scroll Style", "Estilo de desplazamiento"),
|
||||||
("Show Toolbar", ""),
|
("Show Toolbar", "Mostrar herramientas"),
|
||||||
("Hide Toolbar", ""),
|
("Hide Toolbar", "Ocultar herramientas"),
|
||||||
("Direct Connection", "Conexión directa"),
|
("Direct Connection", "Conexión directa"),
|
||||||
("Relay Connection", "Conexión Relay"),
|
("Relay Connection", "Conexión Relay"),
|
||||||
("Secure Connection", "Conexión segura"),
|
("Secure Connection", "Conexión segura"),
|
||||||
@@ -338,7 +338,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Security", "Seguridad"),
|
("Security", "Seguridad"),
|
||||||
("Theme", "Tema"),
|
("Theme", "Tema"),
|
||||||
("Dark Theme", "Tema Oscuro"),
|
("Dark Theme", "Tema Oscuro"),
|
||||||
("Light Theme", ""),
|
("Light Theme", "Tema claro"),
|
||||||
("Dark", "Oscuro"),
|
("Dark", "Oscuro"),
|
||||||
("Light", "Claro"),
|
("Light", "Claro"),
|
||||||
("Follow System", "Tema del sistema"),
|
("Follow System", "Tema del sistema"),
|
||||||
@@ -355,12 +355,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Audio Input Device", "Dispositivo de entrada de audio"),
|
("Audio Input Device", "Dispositivo de entrada de audio"),
|
||||||
("Use IP Whitelisting", "Usar lista de IPs admitidas"),
|
("Use IP Whitelisting", "Usar lista de IPs admitidas"),
|
||||||
("Network", "Red"),
|
("Network", "Red"),
|
||||||
("Pin Toolbar", ""),
|
("Pin Toolbar", "Anclar herramientas"),
|
||||||
("Unpin Toolbar", ""),
|
("Unpin Toolbar", "Desanclar herramientas"),
|
||||||
("Recording", "Grabando"),
|
("Recording", "Grabando"),
|
||||||
("Directory", "Directorio"),
|
("Directory", "Directorio"),
|
||||||
("Automatically record incoming sessions", "Grabación automática de sesiones entrantes"),
|
("Automatically record incoming sessions", "Grabación automática de sesiones entrantes"),
|
||||||
("Automatically record outgoing sessions", ""),
|
("Automatically record outgoing sessions", "Grabación automática de sesiones salientes"),
|
||||||
("Change", "Cambiar"),
|
("Change", "Cambiar"),
|
||||||
("Start session recording", "Comenzar grabación de sesión"),
|
("Start session recording", "Comenzar grabación de sesión"),
|
||||||
("Stop session recording", "Detener grabación de sesión"),
|
("Stop session recording", "Detener grabación de sesión"),
|
||||||
@@ -368,7 +368,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable LAN discovery", "Habilitar descubrimiento de LAN"),
|
("Enable LAN discovery", "Habilitar descubrimiento de LAN"),
|
||||||
("Deny LAN discovery", "Denegar descubrimiento de LAN"),
|
("Deny LAN discovery", "Denegar descubrimiento de LAN"),
|
||||||
("Write a message", "Escribir un mensaje"),
|
("Write a message", "Escribir un mensaje"),
|
||||||
("Prompt", ""),
|
("Prompt", "Solicitud"),
|
||||||
("Please wait for confirmation of UAC...", "Por favor, espera confirmación de UAC"),
|
("Please wait for confirmation of UAC...", "Por favor, espera confirmación de UAC"),
|
||||||
("elevated_foreground_window_tip", "La ventana actual del escritorio remoto necesita privilegios elevados para funcionar, así que no puedes usar ratón y teclado temporalmente. Puedes solicitar al usuario remoto que minimize la ventana actual o hacer clic en el botón de elevación de la ventana de gestión de conexión. Para evitar este problema, se recomienda instalar el programa en el dispositivo remto."),
|
("elevated_foreground_window_tip", "La ventana actual del escritorio remoto necesita privilegios elevados para funcionar, así que no puedes usar ratón y teclado temporalmente. Puedes solicitar al usuario remoto que minimize la ventana actual o hacer clic en el botón de elevación de la ventana de gestión de conexión. Para evitar este problema, se recomienda instalar el programa en el dispositivo remto."),
|
||||||
("Disconnected", "Desconectado"),
|
("Disconnected", "Desconectado"),
|
||||||
@@ -616,9 +616,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("During service is on", "Mientras el servicio está activo"),
|
("During service is on", "Mientras el servicio está activo"),
|
||||||
("Capture screen using DirectX", "Capturar pantalla con DirectX"),
|
("Capture screen using DirectX", "Capturar pantalla con DirectX"),
|
||||||
("Back", "Atrás"),
|
("Back", "Atrás"),
|
||||||
("Apps", ""),
|
("Apps", "Aplicaciones"),
|
||||||
("Volume up", "Bajar volumen"),
|
("Volume up", "Subir volumen"),
|
||||||
("Volume down", "Subir volumen"),
|
("Volume down", "Bajar volumen"),
|
||||||
("Power", "Encendido"),
|
("Power", "Encendido"),
|
||||||
("Telegram bot", "Bot de Telegram"),
|
("Telegram bot", "Bot de Telegram"),
|
||||||
("enable-bot-tip", "Si activas esta característica puedes recibir código 2FA de tu bot. También puede funcionar como notificación de conexión."),
|
("enable-bot-tip", "Si activas esta característica puedes recibir código 2FA de tu bot. También puede funcionar como notificación de conexión."),
|
||||||
@@ -651,7 +651,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Update client clipboard", "Actualizar portapapeles del cliente"),
|
("Update client clipboard", "Actualizar portapapeles del cliente"),
|
||||||
("Untagged", "Sin itiquetar"),
|
("Untagged", "Sin itiquetar"),
|
||||||
("new-version-of-{}-tip", "Hay una nueva versión de {} disponible"),
|
("new-version-of-{}-tip", "Hay una nueva versión de {} disponible"),
|
||||||
("Accessible devices", ""),
|
("Accessible devices", "Dispositivos accesibles"),
|
||||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Por favor, actualiza el cliente RustDesk a la versión {} o superior en el lado remoto"),
|
("upgrade_remote_rustdesk_client_to_{}_tip", "Por favor, actualiza el cliente RustDesk a la versión {} o superior en el lado remoto"),
|
||||||
("d3d_render_tip", "Al activar el renderizado D3D, la pantalla de control remoto puede verse negra en algunos equipos."),
|
("d3d_render_tip", "Al activar el renderizado D3D, la pantalla de control remoto puede verse negra en algunos equipos."),
|
||||||
("Use D3D rendering", "Usar renderizado D3D"),
|
("Use D3D rendering", "Usar renderizado D3D"),
|
||||||
@@ -689,9 +689,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Use WebSocket", "Usar WebSocket"),
|
("Use WebSocket", "Usar WebSocket"),
|
||||||
("Trackpad speed", "Velocidad de trackpad"),
|
("Trackpad speed", "Velocidad de trackpad"),
|
||||||
("Default trackpad speed", "Velocidad predeterminada de trackpad"),
|
("Default trackpad speed", "Velocidad predeterminada de trackpad"),
|
||||||
("Numeric one-time password", ""),
|
("Numeric one-time password", "Contraseña numérica de un solo uso"),
|
||||||
("Enable IPv6 P2P connection", ""),
|
("Enable IPv6 P2P connection", "Habilitar conexión IPv6 P2P"),
|
||||||
("Enable UDP hole punching", ""),
|
("Enable UDP hole punching", "Habilitar perforación de agujero UDP"),
|
||||||
("View camera", "Ver cámara"),
|
("View camera", "Ver cámara"),
|
||||||
("Enable camera", "Habilitar cámara"),
|
("Enable camera", "Habilitar cámara"),
|
||||||
("No cameras", "No hay cámaras"),
|
("No cameras", "No hay cámaras"),
|
||||||
@@ -708,8 +708,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Failed to check if the user is an administrator.", "No se ha podido comprobar si el usuario es un administrador."),
|
("Failed to check if the user is an administrator.", "No se ha podido comprobar si el usuario es un administrador."),
|
||||||
("Supported only in the installed version.", "Soportado solo en la versión instalada."),
|
("Supported only in the installed version.", "Soportado solo en la versión instalada."),
|
||||||
("elevation_username_tip", "Introduzca el nombre de usuario o dominio\\NombreDeUsuario"),
|
("elevation_username_tip", "Introduzca el nombre de usuario o dominio\\NombreDeUsuario"),
|
||||||
("Preparing for installation ...", ""),
|
("Preparing for installation ...", "Preparando instlación..."),
|
||||||
("Show my cursor", ""),
|
("Show my cursor", "Mostrar mi cursor"),
|
||||||
("Scale custom", "Escala personalizada"),
|
("Scale custom", "Escala personalizada"),
|
||||||
("Custom scale slider", "Control deslizante de escala personalizada"),
|
("Custom scale slider", "Control deslizante de escala personalizada"),
|
||||||
("Decrease", "Disminuir"),
|
("Decrease", "Disminuir"),
|
||||||
@@ -721,28 +721,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Show virtual joystick", "Mostrar joystick virtual"),
|
("Show virtual joystick", "Mostrar joystick virtual"),
|
||||||
("Edit note", "Editar nota"),
|
("Edit note", "Editar nota"),
|
||||||
("Alias", ""),
|
("Alias", ""),
|
||||||
("ScrollEdge", ""),
|
("ScrollEdge", "Desplazamiento de pantalla"),
|
||||||
("Allow insecure TLS fallback", ""),
|
("Allow insecure TLS fallback", "Permitir conexión TLS insegura de respaldo"),
|
||||||
("allow-insecure-tls-fallback-tip", ""),
|
("allow-insecure-tls-fallback-tip", "De forma predeterminada, RustDesk verifica el certificado de servidor para protocolos que usen TLS.\nCon esta opción habilitada, Rustdesk volverá al paso de omisión de verificación y procederá en caso de fallo de verificación."),
|
||||||
("Disable UDP", ""),
|
("Disable UDP", "Inhabilitar UDP"),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", "Controla si se usa TCP solamente.\nCuando esta opción está activa, RustDesk no usará más el puerto UDP 21116, en su lugar se usará el TCP 21116."),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", "NOTA: El servidor RustDesk OSS no incluye esta característica."),
|
||||||
("input note here", ""),
|
("input note here", "Introducir nota aquí"),
|
||||||
("note-at-conn-end-tip", ""),
|
("note-at-conn-end-tip", "Pedir nota al finalizar la conexión"),
|
||||||
("Show terminal extra keys", ""),
|
("Show terminal extra keys", "Mostrar teclas extra del terminal"),
|
||||||
("Relative mouse mode", ""),
|
("Relative mouse mode", "Modo de ratón relativo"),
|
||||||
("rel-mouse-not-supported-peer-tip", ""),
|
("rel-mouse-not-supported-peer-tip", "El modo relativo de ratón no está soportado por el par."),
|
||||||
("rel-mouse-not-ready-tip", ""),
|
("rel-mouse-not-ready-tip", "El modo relativo de ratón aún no está preparado. Por favor, inténtalo de nuevo."),
|
||||||
("rel-mouse-lock-failed-tip", ""),
|
("rel-mouse-lock-failed-tip", "Ha fallado el bloqueo del cursor. El modo relativo del ratón ha sido inhabilitado."),
|
||||||
("rel-mouse-exit-{}-tip", ""),
|
("rel-mouse-exit-{}-tip", "Pulsa {} para salir."),
|
||||||
("rel-mouse-permission-lost-tip", ""),
|
("rel-mouse-permission-lost-tip", "Permiso de teclado revocado. El modo relativo del ratón ha sido inhabilitado."),
|
||||||
("Changelog", ""),
|
("Changelog", "Registro de cambios"),
|
||||||
("keep-awake-during-outgoing-sessions-label", ""),
|
("keep-awake-during-outgoing-sessions-label", "Mantener la pantalla activa durante sesiones salientes"),
|
||||||
("keep-awake-during-incoming-sessions-label", ""),
|
("keep-awake-during-incoming-sessions-label", "Mantener la pantalla activa durante sesiones entrantes"),
|
||||||
("Continue with {}", "Continuar con {}"),
|
("Continue with {}", "Continuar con {}"),
|
||||||
("Display Name", ""),
|
("Display Name", "Nombre de pantalla"),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", "La contraseña permanente está ajustada a (oculta)."),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", "Se está usando la contraseña predeterminada."),
|
||||||
("Enable privacy mode", ""),
|
("Enable privacy mode", "Habilitar modo privado"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,6 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Naam Weergeven"),
|
("Display Name", "Naam Weergeven"),
|
||||||
("password-hidden-tip", "Er is een permanent wachtwoord ingesteld (verborgen)."),
|
("password-hidden-tip", "Er is een permanent wachtwoord ingesteld (verborgen)."),
|
||||||
("preset-password-in-use-tip", "Het basis wachtwoord is momenteel in gebruik."),
|
("preset-password-in-use-tip", "Het basis wachtwoord is momenteel in gebruik."),
|
||||||
("Enable privacy mode", "Schakel privacymodus in"),
|
("Enable privacy mode", "Privacymodus inschakelen"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
81
src/main.rs
81
src/main.rs
@@ -38,49 +38,68 @@ fn main() {
|
|||||||
if !common::global_init() {
|
if !common::global_init() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
use clap::App;
|
use clap::{Arg, ArgAction, Command};
|
||||||
use hbb_common::log;
|
use hbb_common::log;
|
||||||
let args = format!(
|
let matches = Command::new("rustdesk")
|
||||||
"-p, --port-forward=[PORT-FORWARD-OPTIONS] 'Format: remote-id:local-port:remote-port[:remote-host]'
|
|
||||||
-c, --connect=[REMOTE_ID] 'test only'
|
|
||||||
-k, --key=[KEY] ''
|
|
||||||
-s, --server=[] 'Start server'",
|
|
||||||
);
|
|
||||||
let matches = App::new("rustdesk")
|
|
||||||
.version(crate::VERSION)
|
.version(crate::VERSION)
|
||||||
.author("Purslane Ltd<info@rustdesk.com>")
|
.author("Purslane Ltd<info@rustdesk.com>")
|
||||||
.about("RustDesk command line tool")
|
.about("RustDesk command line tool")
|
||||||
.args_from_usage(&args)
|
.arg(
|
||||||
|
Arg::new("port-forward")
|
||||||
|
.short('p')
|
||||||
|
.long("port-forward")
|
||||||
|
.value_name("PORT-FORWARD-OPTIONS")
|
||||||
|
.help("Format: remote-id:local-port:remote-port[:remote-host]"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("connect")
|
||||||
|
.short('c')
|
||||||
|
.long("connect")
|
||||||
|
.value_name("REMOTE_ID")
|
||||||
|
.help("test only"),
|
||||||
|
)
|
||||||
|
.arg(Arg::new("key").short('k').long("key").value_name("KEY"))
|
||||||
|
.arg(
|
||||||
|
Arg::new("server")
|
||||||
|
.short('s')
|
||||||
|
.long("server")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.help("Start server"),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
use hbb_common::{config::LocalConfig, env_logger::*};
|
use hbb_common::{config::LocalConfig, env_logger::*};
|
||||||
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
|
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
|
||||||
if let Some(p) = matches.value_of("port-forward") {
|
if let Some(p) = matches.get_one::<String>("port-forward") {
|
||||||
let options: Vec<String> = p.split(":").map(|x| x.to_owned()).collect();
|
let options: Vec<String> = p.split(':').map(|x| x.to_owned()).collect();
|
||||||
if options.len() < 3 {
|
if options.len() < 3 {
|
||||||
log::error!("Wrong port-forward options");
|
log::error!("Wrong port-forward options");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut port = 0;
|
let port = match options[1].parse::<i32>() {
|
||||||
if let Ok(v) = options[1].parse::<i32>() {
|
Ok(v) => v,
|
||||||
port = v;
|
Err(_) => {
|
||||||
} else {
|
log::error!("Wrong local-port");
|
||||||
log::error!("Wrong local-port");
|
return;
|
||||||
return;
|
}
|
||||||
}
|
};
|
||||||
let mut remote_port = 0;
|
let remote_port = match options[2].parse::<i32>() {
|
||||||
if let Ok(v) = options[2].parse::<i32>() {
|
Ok(v) => v,
|
||||||
remote_port = v;
|
Err(_) => {
|
||||||
} else {
|
log::error!("Wrong remote-port");
|
||||||
log::error!("Wrong remote-port");
|
return;
|
||||||
return;
|
}
|
||||||
}
|
};
|
||||||
let mut remote_host = "localhost".to_owned();
|
let mut remote_host = "localhost".to_owned();
|
||||||
if options.len() > 3 {
|
if options.len() > 3 {
|
||||||
remote_host = options[3].clone();
|
remote_host = options[3].clone();
|
||||||
}
|
}
|
||||||
common::test_rendezvous_server();
|
common::test_rendezvous_server();
|
||||||
common::test_nat_type();
|
common::test_nat_type();
|
||||||
let key = matches.value_of("key").unwrap_or("").to_owned();
|
let key = matches
|
||||||
|
.get_one::<String>("key")
|
||||||
|
.map(String::as_str)
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_owned();
|
||||||
let token = LocalConfig::get_option("access_token");
|
let token = LocalConfig::get_option("access_token");
|
||||||
cli::start_one_port_forward(
|
cli::start_one_port_forward(
|
||||||
options[0].clone(),
|
options[0].clone(),
|
||||||
@@ -90,13 +109,17 @@ fn main() {
|
|||||||
key,
|
key,
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
} else if let Some(p) = matches.value_of("connect") {
|
} else if let Some(p) = matches.get_one::<String>("connect") {
|
||||||
common::test_rendezvous_server();
|
common::test_rendezvous_server();
|
||||||
common::test_nat_type();
|
common::test_nat_type();
|
||||||
let key = matches.value_of("key").unwrap_or("").to_owned();
|
let key = matches
|
||||||
|
.get_one::<String>("key")
|
||||||
|
.map(String::as_str)
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_owned();
|
||||||
let token = LocalConfig::get_option("access_token");
|
let token = LocalConfig::get_option("access_token");
|
||||||
cli::connect_test(p, key, token);
|
cli::connect_test(p, key, token);
|
||||||
} else if let Some(p) = matches.value_of("server") {
|
} else if matches.get_flag("server") {
|
||||||
log::info!("id={}", hbb_common::config::Config::get_id());
|
log::info!("id={}", hbb_common::config::Config::get_id());
|
||||||
crate::start_server(true, false);
|
crate::start_server(true, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,15 +38,45 @@ 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);
|
||||||
static SENT_REGISTER_PK: AtomicBool = AtomicBool::new(false);
|
static SENT_REGISTER_PK: AtomicBool = AtomicBool::new(false);
|
||||||
|
pub(crate) static NEEDS_DEPLOY: AtomicBool = AtomicBool::new(false);
|
||||||
|
// register_pk retry interval (ms) when device is awaiting deployment
|
||||||
|
const DEPLOY_RETRY_INTERVAL: i64 = 30_000;
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref LAST_NOT_DEPLOYED_REGISTER: Mutex<Option<Instant>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single source of truth for the "awaiting deployment" backoff. The server has
|
||||||
|
// already told us this device is not in its db; until the operator runs
|
||||||
|
// `rustdesk --deploy --token <api_token>` there is no point re-running the
|
||||||
|
// register path more often than DEPLOY_RETRY_INTERVAL. Gating in the timer
|
||||||
|
// loops (rather than only inside register_pk) also avoids the
|
||||||
|
// last_register_sent / fails / latency / UDP-rebind churn the loop would
|
||||||
|
// otherwise spin on while no response ever comes back.
|
||||||
|
async fn deploy_register_throttled() -> bool {
|
||||||
|
if !NEEDS_DEPLOY.load(Ordering::SeqCst) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LAST_NOT_DEPLOYED_REGISTER
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.map(|t| (t.elapsed().as_millis() as i64) < DEPLOY_RETRY_INTERVAL)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RendezvousMediator {
|
pub struct RendezvousMediator {
|
||||||
@@ -48,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 {
|
||||||
@@ -158,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));
|
||||||
@@ -222,10 +261,21 @@ 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;
|
||||||
}
|
}
|
||||||
|
// The server already told us this device is not deployed. Skip
|
||||||
|
// the whole register / fails / latency / UDP-rebind path until
|
||||||
|
// DEPLOY_RETRY_INTERVAL elapses, otherwise the loop spins every
|
||||||
|
// few seconds (log spam + misapplied network-recovery rebind)
|
||||||
|
// until the operator runs `rustdesk --deploy`.
|
||||||
|
if deploy_register_throttled().await {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let now = Some(Instant::now());
|
let now = Some(Instant::now());
|
||||||
let expired = last_register_resp.map(|x| x.elapsed().as_millis() as i64 >= REG_INTERVAL).unwrap_or(true);
|
let expired = last_register_resp.map(|x| x.elapsed().as_millis() as i64 >= REG_INTERVAL).unwrap_or(true);
|
||||||
let timeout = last_register_sent.map(|x| x.elapsed().as_millis() as i64 >= reg_timeout).unwrap_or(false);
|
let timeout = last_register_sent.map(|x| x.elapsed().as_millis() as i64 >= reg_timeout).unwrap_or(false);
|
||||||
@@ -289,10 +339,22 @@ impl RendezvousMediator {
|
|||||||
Config::set_key_confirmed(true);
|
Config::set_key_confirmed(true);
|
||||||
Config::set_host_key_confirmed(&self.host_prefix, true);
|
Config::set_host_key_confirmed(&self.host_prefix, true);
|
||||||
*SOLVING_PK_MISMATCH.lock().await = "".to_owned();
|
*SOLVING_PK_MISMATCH.lock().await = "".to_owned();
|
||||||
|
NEEDS_DEPLOY.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
Ok(register_pk_response::Result::UUID_MISMATCH) => {
|
Ok(register_pk_response::Result::UUID_MISMATCH) => {
|
||||||
self.handle_uuid_mismatch(sink).await?;
|
self.handle_uuid_mismatch(sink).await?;
|
||||||
}
|
}
|
||||||
|
Ok(register_pk_response::Result::NOT_DEPLOYED) => {
|
||||||
|
if !NEEDS_DEPLOY.load(Ordering::SeqCst) {
|
||||||
|
log::warn!("Server requires deployment. Run `rustdesk --deploy --token <api_token>` on this device.");
|
||||||
|
}
|
||||||
|
NEEDS_DEPLOY.store(true, Ordering::SeqCst);
|
||||||
|
// Clear key_confirmed so the UI reflects the truth: this device is
|
||||||
|
// not currently registered. Covers the case where an online device
|
||||||
|
// was deleted by an admin while running.
|
||||||
|
Config::set_key_confirmed(false);
|
||||||
|
Config::set_host_key_confirmed(&self.host_prefix, false);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("unknown RegisterPkResponse");
|
log::error!("unknown RegisterPkResponse");
|
||||||
}
|
}
|
||||||
@@ -323,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(
|
||||||
@@ -345,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;
|
||||||
@@ -377,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;
|
||||||
@@ -428,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
|
||||||
@@ -442,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);
|
||||||
@@ -460,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 {
|
||||||
@@ -527,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
|
||||||
@@ -569,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;
|
||||||
@@ -580,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,
|
||||||
@@ -607,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;
|
||||||
@@ -620,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 {
|
||||||
@@ -678,6 +867,21 @@ impl RendezvousMediator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn register_pk(&mut self, socket: Sink<'_>) -> ResultType<()> {
|
async fn register_pk(&mut self, socket: Sink<'_>) -> ResultType<()> {
|
||||||
|
// Throttle register_pk when the device is awaiting deployment: server
|
||||||
|
// already told us we're not in its db; sending more often than every
|
||||||
|
// DEPLOY_RETRY_INTERVAL ms is wasted traffic until the operator runs
|
||||||
|
// `rustdesk --deploy --token <api_token>`.
|
||||||
|
if NEEDS_DEPLOY.load(Ordering::SeqCst) {
|
||||||
|
let mut last = LAST_NOT_DEPLOYED_REGISTER.lock().await;
|
||||||
|
if let Some(t) = *last {
|
||||||
|
if (t.elapsed().as_millis() as i64) < DEPLOY_RETRY_INTERVAL {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*last = Some(Instant::now());
|
||||||
|
} else {
|
||||||
|
*LAST_NOT_DEPLOYED_REGISTER.lock().await = None;
|
||||||
|
}
|
||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
let pk = Config::get_key_pair().1;
|
let pk = Config::get_key_pair().1;
|
||||||
let uuid = hbb_common::get_uuid();
|
let uuid = hbb_common::get_uuid();
|
||||||
|
|||||||
Reference in New Issue
Block a user