Compare commits
11 Commits
d52ef0de30
...
0c49f9a29c
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c49f9a29c | |||
| 83419b6549 | |||
| 6338940d7a | |||
| 7cf11f7b77 | |||
| 073bae1e73 | |||
| 16900b9b06 | |||
| 33487a0b14 | |||
| f94753bddb | |||
| e608898d08 | |||
| 0d7f746291 | |||
| f7f0e19223 |
@@ -78,7 +78,7 @@ message LoginRequest {
|
||||
oneof union {
|
||||
FileTransfer file_transfer = 7;
|
||||
PortForward port_forward = 8;
|
||||
ViewCamera view_camera = 9;
|
||||
ViewCamera view_camera = 15;
|
||||
}
|
||||
bool video_ack_required = 9;
|
||||
uint64 session_id = 10;
|
||||
|
||||
@@ -170,6 +170,29 @@ message HealthCheck {
|
||||
string token = 1;
|
||||
}
|
||||
|
||||
// Backported from upstream rustdesk/hbb_common @ 87b11a7 so the OSS hbbs
|
||||
// can implement the HTTP-over-rendezvous fallback the client uses when
|
||||
// OPTION_USE_RAW_TCP_FOR_API=Y. Wire-compatible with the client; only the
|
||||
// three message types and tags 27/28 are added.
|
||||
message HeaderEntry {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message HttpProxyRequest {
|
||||
string method = 1;
|
||||
string path = 2;
|
||||
repeated HeaderEntry headers = 3;
|
||||
bytes body = 4;
|
||||
}
|
||||
|
||||
message HttpProxyResponse {
|
||||
int32 status = 1;
|
||||
repeated HeaderEntry headers = 2;
|
||||
bytes body = 3;
|
||||
string error = 4;
|
||||
}
|
||||
|
||||
message RendezvousMessage {
|
||||
oneof union {
|
||||
RegisterPeer register_peer = 6;
|
||||
@@ -193,5 +216,7 @@ message RendezvousMessage {
|
||||
OnlineResponse online_response = 24;
|
||||
KeyExchange key_exchange = 25;
|
||||
HealthCheck hc = 26;
|
||||
HttpProxyRequest http_proxy_request = 27;
|
||||
HttpProxyResponse http_proxy_response = 28;
|
||||
}
|
||||
}
|
||||
|
||||
+147
-45
@@ -1214,7 +1214,7 @@ impl PeerConfig {
|
||||
}
|
||||
}
|
||||
if store {
|
||||
config.store(id);
|
||||
config.store_(id);
|
||||
}
|
||||
config
|
||||
}
|
||||
@@ -1232,6 +1232,10 @@ impl PeerConfig {
|
||||
|
||||
pub fn store(&self, id: &str) {
|
||||
let _lock = CONFIG.read().unwrap();
|
||||
self.store_(id);
|
||||
}
|
||||
|
||||
fn store_(&self, id: &str) {
|
||||
let mut config = self.clone();
|
||||
config.password =
|
||||
encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||
@@ -1269,55 +1273,151 @@ impl PeerConfig {
|
||||
Config::with_extension(Config::path(path))
|
||||
}
|
||||
|
||||
pub fn peers(id_filters: Option<Vec<String>>) -> Vec<(String, SystemTime, PeerConfig)> {
|
||||
if let Ok(peers) = Config::path(PEERS).read_dir() {
|
||||
if let Ok(peers) = peers
|
||||
.map(|res| res.map(|e| e.path()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
{
|
||||
let mut peers: Vec<_> = peers
|
||||
.iter()
|
||||
.filter(|p| {
|
||||
p.is_file()
|
||||
&& p.extension().map(|p| p.to_str().unwrap_or("")) == Some("toml")
|
||||
})
|
||||
.map(|p| {
|
||||
let id = p
|
||||
.file_stem()
|
||||
.map(|p| p.to_str().unwrap_or(""))
|
||||
.unwrap_or("")
|
||||
.to_owned();
|
||||
// The number of peers to load in the first round when showing the peers card list in the main window.
|
||||
// When there're too many peers, loading all of them at once will take a long time.
|
||||
// We can load them in two rouds, the first round loads the first 100 peers, and the second round loads the rest.
|
||||
// Then the UI will show the first 100 peers first, and the rest will be loaded and shown later.
|
||||
pub const BATCH_LOADING_COUNT: usize = 100;
|
||||
|
||||
let id_decoded_string = if id.starts_with("base64_") && id.len() != 7 {
|
||||
let id_decoded = base64::decode(&id[7..], base64::Variant::Original)
|
||||
.unwrap_or_default();
|
||||
String::from_utf8_lossy(&id_decoded).as_ref().to_owned()
|
||||
pub fn get_vec_id_modified_time_path(
|
||||
id_filters: &Option<Vec<String>>,
|
||||
) -> Vec<(String, SystemTime, PathBuf)> {
|
||||
if let Ok(peers) = Config::path(PEERS).read_dir() {
|
||||
let mut vec_id_modified_time_path = peers
|
||||
.into_iter()
|
||||
.filter_map(|res| match res {
|
||||
Ok(res) => {
|
||||
let p = res.path();
|
||||
if p.is_file()
|
||||
&& p.extension().map(|p| p.to_str().unwrap_or("")) == Some("toml")
|
||||
{
|
||||
Some(p)
|
||||
} else {
|
||||
id
|
||||
};
|
||||
(id_decoded_string, p)
|
||||
})
|
||||
.filter(|(id, _)| {
|
||||
let Some(filters) = &id_filters else {
|
||||
return true;
|
||||
};
|
||||
filters.contains(id)
|
||||
})
|
||||
.map(|(id, p)| {
|
||||
let t = crate::get_modified_time(p);
|
||||
let c = PeerConfig::load(&id);
|
||||
if c.info.platform.is_empty() {
|
||||
fs::remove_file(p).ok();
|
||||
None
|
||||
}
|
||||
(id, t, c)
|
||||
})
|
||||
.filter(|p| !p.2.info.platform.is_empty())
|
||||
.collect();
|
||||
peers.sort_unstable_by(|a, b| b.1.cmp(&a.1));
|
||||
return peers;
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|p| {
|
||||
let id = p
|
||||
.file_stem()
|
||||
.map(|p| p.to_str().unwrap_or(""))
|
||||
.unwrap_or("")
|
||||
.to_owned();
|
||||
|
||||
let id_decoded_string = if id.starts_with("base64_") && id.len() != 7 {
|
||||
let id_decoded =
|
||||
base64::decode(&id[7..], base64::Variant::Original).unwrap_or_default();
|
||||
String::from_utf8_lossy(&id_decoded).as_ref().to_owned()
|
||||
} else {
|
||||
id
|
||||
};
|
||||
(id_decoded_string, p)
|
||||
})
|
||||
.filter(|(id, _)| {
|
||||
let Some(filters) = id_filters else {
|
||||
return true;
|
||||
};
|
||||
filters.contains(id)
|
||||
})
|
||||
.map(|(id, p)| {
|
||||
let t = crate::get_modified_time(&p);
|
||||
(id, t, p)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
vec_id_modified_time_path.sort_unstable_by(|a, b| b.1.cmp(&a.1));
|
||||
vec_id_modified_time_path
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn preload_file_async(path: PathBuf) {
|
||||
let _ = tokio::fs::File::open(path).await;
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn preload_peers_async() {
|
||||
let now = std::time::Instant::now();
|
||||
let vec_id_modified_time_path = Self::get_vec_id_modified_time_path(&None);
|
||||
let total_count = vec_id_modified_time_path.len();
|
||||
let mut futs = vec![];
|
||||
for (_, _, path) in vec_id_modified_time_path.into_iter() {
|
||||
futs.push(Self::preload_file_async(path));
|
||||
if futs.len() >= Self::BATCH_LOADING_COUNT {
|
||||
let first_load_start = std::time::Instant::now();
|
||||
futures::future::join_all(futs).await;
|
||||
if first_load_start.elapsed().as_millis() < 10 {
|
||||
// No need to preload the rest if the first load is fast.
|
||||
return;
|
||||
}
|
||||
futs = vec![];
|
||||
}
|
||||
}
|
||||
Default::default()
|
||||
if !futs.is_empty() {
|
||||
futures::future::join_all(futs).await;
|
||||
}
|
||||
log::info!(
|
||||
"Preload peers done in {:?}, batch_count: {}, total: {}",
|
||||
now.elapsed(),
|
||||
Self::BATCH_LOADING_COUNT,
|
||||
total_count
|
||||
);
|
||||
}
|
||||
|
||||
// We have to preload all peers in a background thread.
|
||||
// Because we find that opening files the first time after the system (Windows) booting will be very slow, up to 200~400ms.
|
||||
// The reason is that the Windows has "Microsoft Defender Antivirus Service" running in the background, which will scan the file when it's opened the first time.
|
||||
// So we have to preload all peers in a background thread to avoid the delay when opening the file the first time.
|
||||
// We can temporarily stop "Microsoft Defender Antivirus Service" or add the fold to the white list, to verify this. But don't do this in the release version.
|
||||
pub fn preload_peers() {
|
||||
std::thread::spawn(|| {
|
||||
Self::preload_peers_async();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn peers(id_filters: Option<Vec<String>>) -> Vec<(String, SystemTime, PeerConfig)> {
|
||||
let vec_id_modified_time_path = Self::get_vec_id_modified_time_path(&id_filters);
|
||||
Self::batch_peers(
|
||||
&vec_id_modified_time_path,
|
||||
0,
|
||||
Some(vec_id_modified_time_path.len()),
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn batch_peers(
|
||||
all: &Vec<(String, SystemTime, PathBuf)>,
|
||||
from: usize,
|
||||
to: Option<usize>,
|
||||
) -> (Vec<(String, SystemTime, PeerConfig)>, usize) {
|
||||
if from >= all.len() {
|
||||
return (vec![], 0);
|
||||
}
|
||||
|
||||
let to = match to {
|
||||
Some(to) => to.min(all.len()),
|
||||
None => (from + Self::BATCH_LOADING_COUNT).min(all.len()),
|
||||
};
|
||||
|
||||
// to <= from is unexpected, but we can just return an empty vec in this case.
|
||||
if to <= from {
|
||||
return (vec![], from);
|
||||
}
|
||||
|
||||
let peers: Vec<_> = all[from..to]
|
||||
.iter()
|
||||
.map(|(id, t, p)| {
|
||||
let c = PeerConfig::load(&id);
|
||||
if c.info.platform.is_empty() {
|
||||
fs::remove_file(p).ok();
|
||||
}
|
||||
(id.clone(), t.clone(), c)
|
||||
})
|
||||
.filter(|p| !p.2.info.platform.is_empty())
|
||||
.collect();
|
||||
(peers, to)
|
||||
}
|
||||
|
||||
pub fn exists(id: &str) -> bool {
|
||||
@@ -2265,6 +2365,7 @@ pub mod keys {
|
||||
pub const OPTION_ONE_WAY_CLIPBOARD_REDIRECTION: &str = "one-way-clipboard-redirection";
|
||||
pub const OPTION_ALLOW_LOGON_SCREEN_PASSWORD: &str = "allow-logon-screen-password";
|
||||
pub const OPTION_ONE_WAY_FILE_TRANSFER: &str = "one-way-file-transfer";
|
||||
pub const OPTION_ALLOW_HTTPS_21114: &str = "allow-https-21114";
|
||||
|
||||
// flutter local options
|
||||
pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState";
|
||||
@@ -2417,6 +2518,7 @@ pub mod keys {
|
||||
OPTION_ONE_WAY_CLIPBOARD_REDIRECTION,
|
||||
OPTION_ALLOW_LOGON_SCREEN_PASSWORD,
|
||||
OPTION_ONE_WAY_FILE_TRANSFER,
|
||||
OPTION_ALLOW_HTTPS_21114,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -68,10 +68,11 @@ pub(crate) fn new_socket(addr: std::net::SocketAddr, reuse: bool) -> Result<TcpS
|
||||
std::net::SocketAddr::V6(..) => TcpSocket::new_v6()?,
|
||||
};
|
||||
if reuse {
|
||||
// windows has no reuse_port, but it's reuse_address
|
||||
// windows has no reuse_port, but its reuse_address
|
||||
// almost equals to unix's reuse_port + reuse_address,
|
||||
// though may introduce nondeterministic behavior
|
||||
#[cfg(unix)]
|
||||
// illumos has no support for SO_REUSEPORT
|
||||
#[cfg(all(unix, not(target_os = "illumos")))]
|
||||
socket.set_reuseport(true).ok();
|
||||
socket.set_reuseaddr(true).ok();
|
||||
}
|
||||
@@ -226,6 +227,8 @@ pub async fn listen_any(port: u16) -> ResultType<TcpListener> {
|
||||
if let Ok(mut socket) = TcpSocket::new_v6() {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// illumos has no support for SO_REUSEPORT
|
||||
#[cfg(not(target_os = "illumos"))]
|
||||
socket.set_reuseport(true).ok();
|
||||
socket.set_reuseaddr(true).ok();
|
||||
use std::os::unix::io::{FromRawFd, IntoRawFd};
|
||||
|
||||
+3
-2
@@ -20,10 +20,11 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result<Socket,
|
||||
SocketAddr::V6(..) => Socket::new(Domain::ipv6(), Type::dgram(), None),
|
||||
}?;
|
||||
if reuse {
|
||||
// windows has no reuse_port, but it's reuse_address
|
||||
// windows has no reuse_port, but its reuse_address
|
||||
// almost equals to unix's reuse_port + reuse_address,
|
||||
// though may introduce nondeterministic behavior
|
||||
#[cfg(unix)]
|
||||
// illumos has no support for SO_REUSEPORT
|
||||
#[cfg(all(unix, not(target_os = "illumos")))]
|
||||
socket.set_reuse_port(true).ok();
|
||||
socket.set_reuse_address(true).ok();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user