mirror of
https://github.com/rustdesk/rustdesk-server.git
synced 2024-11-27 23:29:00 +08:00
192 lines
5.5 KiB
Rust
192 lines
5.5 KiB
Rust
use clap::App;
|
|
use hbb_common::{
|
|
anyhow::{Context, Result},
|
|
log, ResultType,
|
|
};
|
|
use ini::Ini;
|
|
use sodiumoxide::crypto::sign;
|
|
use std::{
|
|
io::prelude::*,
|
|
io::Read,
|
|
net::SocketAddr,
|
|
time::{Instant, SystemTime},
|
|
};
|
|
|
|
#[allow(dead_code)]
|
|
pub(crate) fn get_expired_time() -> Instant {
|
|
let now = Instant::now();
|
|
now.checked_sub(std::time::Duration::from_secs(3600))
|
|
.unwrap_or(now)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(crate) fn test_if_valid_server(host: &str, name: &str) -> ResultType<SocketAddr> {
|
|
use std::net::ToSocketAddrs;
|
|
let res = if host.contains(':') {
|
|
host.to_socket_addrs()?.next().context("")
|
|
} else {
|
|
format!("{}:{}", host, 0)
|
|
.to_socket_addrs()?
|
|
.next()
|
|
.context("")
|
|
};
|
|
if res.is_err() {
|
|
log::error!("Invalid {} {}: {:?}", name, host, res);
|
|
}
|
|
res
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(crate) fn get_servers(s: &str, tag: &str) -> Vec<String> {
|
|
let servers: Vec<String> = s
|
|
.split(',')
|
|
.filter(|x| !x.is_empty() && test_if_valid_server(x, tag).is_ok())
|
|
.map(|x| x.to_owned())
|
|
.collect();
|
|
log::info!("{}={:?}", tag, servers);
|
|
servers
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[inline]
|
|
fn arg_name(name: &str) -> String {
|
|
name.to_uppercase().replace('_', "-")
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn init_args(args: &str, name: &str, about: &str) {
|
|
let matches = App::new(name)
|
|
.version(crate::version::VERSION)
|
|
.author("Purslane Ltd. <info@rustdesk.com>")
|
|
.about(about)
|
|
.args_from_usage(args)
|
|
.get_matches();
|
|
if let Ok(v) = Ini::load_from_file(".env") {
|
|
if let Some(section) = v.section(None::<String>) {
|
|
section
|
|
.iter()
|
|
.for_each(|(k, v)| std::env::set_var(arg_name(k), v));
|
|
}
|
|
}
|
|
if let Some(config) = matches.value_of("config") {
|
|
if let Ok(v) = Ini::load_from_file(config) {
|
|
if let Some(section) = v.section(None::<String>) {
|
|
section
|
|
.iter()
|
|
.for_each(|(k, v)| std::env::set_var(arg_name(k), v));
|
|
}
|
|
}
|
|
}
|
|
for (k, v) in matches.args {
|
|
if let Some(v) = v.vals.get(0) {
|
|
std::env::set_var(arg_name(k), v.to_string_lossy().to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[inline]
|
|
pub fn get_arg(name: &str) -> String {
|
|
get_arg_or(name, "".to_owned())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[inline]
|
|
pub fn get_arg_or(name: &str, default: String) -> String {
|
|
std::env::var(arg_name(name)).unwrap_or(default)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[inline]
|
|
pub fn now() -> u64 {
|
|
SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map(|x| x.as_secs())
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
pub fn gen_sk(wait: u64) -> (String, Option<sign::SecretKey>) {
|
|
let sk_file = "id_ed25519";
|
|
if wait > 0 && !std::path::Path::new(sk_file).exists() {
|
|
std::thread::sleep(std::time::Duration::from_millis(wait));
|
|
}
|
|
if let Ok(mut file) = std::fs::File::open(sk_file) {
|
|
let mut contents = String::new();
|
|
if file.read_to_string(&mut contents).is_ok() {
|
|
let sk = base64::decode(&contents).unwrap_or_default();
|
|
if sk.len() == sign::SECRETKEYBYTES {
|
|
let mut tmp = [0u8; sign::SECRETKEYBYTES];
|
|
tmp[..].copy_from_slice(&sk);
|
|
let pk = base64::encode(&tmp[sign::SECRETKEYBYTES / 2..]);
|
|
log::info!("Private key comes from {}", sk_file);
|
|
return (pk, Some(sign::SecretKey(tmp)));
|
|
}
|
|
}
|
|
} else {
|
|
let gen_func = || {
|
|
let (tmp, sk) = sign::gen_keypair();
|
|
(base64::encode(tmp), sk)
|
|
};
|
|
let (mut pk, mut sk) = gen_func();
|
|
for _ in 0..300 {
|
|
if !pk.contains('/') && !pk.contains(':') {
|
|
break;
|
|
}
|
|
(pk, sk) = gen_func();
|
|
}
|
|
let pub_file = format!("{sk_file}.pub");
|
|
if let Ok(mut f) = std::fs::File::create(&pub_file) {
|
|
f.write_all(pk.as_bytes()).ok();
|
|
if let Ok(mut f) = std::fs::File::create(sk_file) {
|
|
let s = base64::encode(&sk);
|
|
if f.write_all(s.as_bytes()).is_ok() {
|
|
log::info!("Private/public key written to {}/{}", sk_file, pub_file);
|
|
log::debug!("Public key: {}", pk);
|
|
return (pk, Some(sk));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
("".to_owned(), None)
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
pub async fn listen_signal() -> Result<()> {
|
|
use hbb_common::tokio;
|
|
use hbb_common::tokio::signal::unix::{signal, SignalKind};
|
|
|
|
tokio::spawn(async {
|
|
let mut s = signal(SignalKind::hangup())?;
|
|
let hangup = s.recv();
|
|
let mut s = signal(SignalKind::terminate())?;
|
|
let terminate = s.recv();
|
|
let mut s = signal(SignalKind::interrupt())?;
|
|
let interrupt = s.recv();
|
|
let mut s = signal(SignalKind::quit())?;
|
|
let quit = s.recv();
|
|
|
|
tokio::select! {
|
|
_ = hangup => {
|
|
log::info!("signal hangup");
|
|
}
|
|
_ = terminate => {
|
|
log::info!("signal terminate");
|
|
}
|
|
_ = interrupt => {
|
|
log::info!("signal interrupt");
|
|
}
|
|
_ = quit => {
|
|
log::info!("signal quit");
|
|
}
|
|
}
|
|
Ok(())
|
|
})
|
|
.await?
|
|
}
|
|
|
|
#[cfg(not(unix))]
|
|
pub async fn listen_signal() -> Result<()> {
|
|
let () = std::future::pending().await;
|
|
unreachable!();
|
|
}
|