mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-17 11:34:48 +03:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d26100db01 | ||
|
|
0851cf4d38 | ||
|
|
66e39abb74 | ||
|
|
95758b1a47 | ||
|
|
2685a25e51 | ||
|
|
2643f00216 | ||
|
|
e222127009 |
@@ -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")
|
||||||
|
|||||||
@@ -41,6 +41,30 @@ lazy_static::lazy_static! {
|
|||||||
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 {
|
||||||
@@ -226,6 +250,14 @@ impl RendezvousMediator {
|
|||||||
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 +321,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");
|
||||||
}
|
}
|
||||||
@@ -678,6 +722,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