sync with rustdesk-server hbb_common

This commit is contained in:
rustdesk 2023-01-27 11:42:08 +08:00
parent b07f626611
commit a8c3499d7b
12 changed files with 913 additions and 923 deletions

View File

@ -8,10 +8,7 @@ fn main() {
.out_dir(out_dir) .out_dir(out_dir)
.inputs(&["protos/rendezvous.proto", "protos/message.proto"]) .inputs(&["protos/rendezvous.proto", "protos/message.proto"])
.include("protos") .include("protos")
.customize( .customize(protobuf_codegen::Customize::default().tokio_bytes(true))
protobuf_codegen::Customize::default()
.tokio_bytes(true)
)
.run() .run()
.expect("Codegen failed."); .expect("Codegen failed.");
} }

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,12 @@ enum DecodeState {
Data(usize), Data(usize),
} }
impl Default for BytesCodec {
fn default() -> Self {
Self::new()
}
}
impl BytesCodec { impl BytesCodec {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -56,7 +62,7 @@ impl BytesCodec {
} }
src.advance(head_len); src.advance(head_len);
src.reserve(n); src.reserve(n);
return Ok(Some(n)); Ok(Some(n))
} }
fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> { fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {

View File

@ -32,12 +32,7 @@ pub fn decompress(data: &[u8]) -> Vec<u8> {
const MAX: usize = 1024 * 1024 * 64; const MAX: usize = 1024 * 1024 * 64;
const MIN: usize = 1024 * 1024; const MIN: usize = 1024 * 1024;
let mut n = 30 * data.len(); let mut n = 30 * data.len();
if n > MAX { n = n.clamp(MIN, MAX);
n = MAX;
}
if n < MIN {
n = MIN;
}
match d.decompress(data, n) { match d.decompress(data, n) {
Ok(res) => out = res, Ok(res) => out = res,
Err(err) => { Err(err) => {

View File

@ -29,7 +29,7 @@ pub const READ_TIMEOUT: u64 = 30_000;
pub const REG_INTERVAL: i64 = 12_000; pub const REG_INTERVAL: i64 = 12_000;
pub const COMPRESS_LEVEL: i32 = 3; pub const COMPRESS_LEVEL: i32 = 3;
const SERIAL: i32 = 3; const SERIAL: i32 = 3;
const PASSWORD_ENC_VERSION: &'static str = "00"; const PASSWORD_ENC_VERSION: &str = "00";
// 128x128 // 128x128
#[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding #[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding
pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII= pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII=
@ -43,6 +43,7 @@ lazy_static::lazy_static! {
} }
type Size = (i32, i32, i32, i32); type Size = (i32, i32, i32, i32);
type KeyPair = (Vec<u8>, Vec<u8>);
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref CONFIG: Arc<RwLock<Config>> = Arc::new(RwLock::new(Config::load())); static ref CONFIG: Arc<RwLock<Config>> = Arc::new(RwLock::new(Config::load()));
@ -54,7 +55,7 @@ lazy_static::lazy_static! {
_ => "", _ => "",
}.to_owned())); }.to_owned()));
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned())); pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
static ref KEY_PAIR: Arc<Mutex<Option<(Vec<u8>, Vec<u8>)>>> = Default::default(); static ref KEY_PAIR: Arc<Mutex<Option<KeyPair>>> = Default::default();
static ref HW_CODEC_CONFIG: Arc<RwLock<HwCodecConfig>> = Arc::new(RwLock::new(HwCodecConfig::load())); static ref HW_CODEC_CONFIG: Arc<RwLock<HwCodecConfig>> = Arc::new(RwLock::new(HwCodecConfig::load()));
} }
@ -75,18 +76,18 @@ lazy_static::lazy_static! {
]); ]);
} }
const CHARS: &'static [char] = &[ const CHARS: &[char] = &[
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
]; ];
const RENDEZVOUS_SERVERS: &'static [&'static str] = &[ pub const RENDEZVOUS_SERVERS: &[&str] = &[
"rs-ny.rustdesk.com", "rs-ny.rustdesk.com",
"rs-sg.rustdesk.com", "rs-sg.rustdesk.com",
"rs-cn.rustdesk.com", "rs-cn.rustdesk.com",
]; ];
pub const RS_PUB_KEY: &'static str = match option_env!("RS_PUB_KEY") { pub const RS_PUB_KEY: &str = match option_env!("RS_PUB_KEY") {
Some(key) if !key.is_empty() => key, Some(key) if !key.is_empty() => key,
_ => "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=", _ => "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=",
}; };
@ -131,7 +132,7 @@ pub struct Config {
#[serde(default)] #[serde(default)]
salt: String, salt: String,
#[serde(default)] #[serde(default)]
key_pair: (Vec<u8>, Vec<u8>), // sk, pk key_pair: KeyPair, // sk, pk
#[serde(default)] #[serde(default)]
key_confirmed: bool, key_confirmed: bool,
#[serde(default)] #[serde(default)]
@ -319,7 +320,7 @@ impl Config2 {
pub fn load_path<T: serde::Serialize + serde::de::DeserializeOwned + Default + std::fmt::Debug>( pub fn load_path<T: serde::Serialize + serde::de::DeserializeOwned + Default + std::fmt::Debug>(
file: PathBuf, file: PathBuf,
) -> T { ) -> T {
let cfg = match confy::load_path(&file) { let cfg = match confy::load_path(file) {
Ok(config) => config, Ok(config) => config,
Err(err) => { Err(err) => {
log::error!("Failed to load config: {}", err); log::error!("Failed to load config: {}", err);
@ -366,20 +367,16 @@ impl Config {
config.id = id; config.id = id;
id_valid = true; id_valid = true;
store |= store2; store |= store2;
} else { } else if crate::get_modified_time(&Self::file_(""))
if crate::get_modified_time(&Self::file_("")) .checked_sub(std::time::Duration::from_secs(30)) // allow modification during installation
.checked_sub(std::time::Duration::from_secs(30)) // allow modification during installation .unwrap_or_else(crate::get_exe_time)
.unwrap_or(crate::get_exe_time()) < crate::get_exe_time()
< crate::get_exe_time() && !config.id.is_empty()
{ && config.enc_id.is_empty()
if !config.id.is_empty() && !decrypt_str_or_original(&config.id, PASSWORD_ENC_VERSION).1
&& config.enc_id.is_empty() {
&& !decrypt_str_or_original(&config.id, PASSWORD_ENC_VERSION).1 id_valid = true;
{ store = true;
id_valid = true;
store = true;
}
}
} }
if !id_valid { if !id_valid {
for _ in 0..3 { for _ in 0..3 {
@ -444,18 +441,18 @@ impl Config {
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
{ {
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
let org = ""; let org = "".to_owned();
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let org = ORG.read().unwrap().clone(); let org = ORG.read().unwrap().clone();
// /var/root for root // /var/root for root
if let Some(project) = if let Some(project) =
directories_next::ProjectDirs::from("", &org, &*APP_NAME.read().unwrap()) directories_next::ProjectDirs::from("", &org, &APP_NAME.read().unwrap())
{ {
let mut path = patch(project.config_dir().to_path_buf()); let mut path = patch(project.config_dir().to_path_buf());
path.push(p); path.push(p);
return path; return path;
} }
return "".into(); "".into()
} }
} }
@ -539,9 +536,9 @@ impl Config {
rendezvous_server = Self::get_rendezvous_servers() rendezvous_server = Self::get_rendezvous_servers()
.drain(..) .drain(..)
.next() .next()
.unwrap_or("".to_owned()); .unwrap_or_default();
} }
if !rendezvous_server.contains(":") { if !rendezvous_server.contains(':') {
rendezvous_server = format!("{}:{}", rendezvous_server, RENDEZVOUS_PORT); rendezvous_server = format!("{}:{}", rendezvous_server, RENDEZVOUS_PORT);
} }
rendezvous_server rendezvous_server
@ -559,8 +556,8 @@ impl Config {
let serial_obsolute = CONFIG2.read().unwrap().serial > SERIAL; let serial_obsolute = CONFIG2.read().unwrap().serial > SERIAL;
if serial_obsolute { if serial_obsolute {
let ss: Vec<String> = Self::get_option("rendezvous-servers") let ss: Vec<String> = Self::get_option("rendezvous-servers")
.split(",") .split(',')
.filter(|x| x.contains(".")) .filter(|x| x.contains('.'))
.map(|x| x.to_owned()) .map(|x| x.to_owned())
.collect(); .collect();
if !ss.is_empty() { if !ss.is_empty() {
@ -580,7 +577,7 @@ impl Config {
let mut delay = i64::MAX; let mut delay = i64::MAX;
for (tmp_host, tmp_delay) in ONLINE.lock().unwrap().iter() { for (tmp_host, tmp_delay) in ONLINE.lock().unwrap().iter() {
if tmp_delay > &0 && tmp_delay < &delay { if tmp_delay > &0 && tmp_delay < &delay {
delay = tmp_delay.clone(); delay = *tmp_delay;
host = tmp_host.to_string(); host = tmp_host.to_string();
} }
} }
@ -647,7 +644,7 @@ impl Config {
for x in &ma.bytes()[2..] { for x in &ma.bytes()[2..] {
id = (id << 8) | (*x as u32); id = (id << 8) | (*x as u32);
} }
id = id & 0x1FFFFFFF; id &= 0x1FFFFFFF;
Some(id.to_string()) Some(id.to_string())
} else { } else {
None None
@ -679,11 +676,7 @@ impl Config {
} }
pub fn get_host_key_confirmed(host: &str) -> bool { pub fn get_host_key_confirmed(host: &str) -> bool {
if let Some(true) = CONFIG.read().unwrap().keys_confirmed.get(host) { matches!(CONFIG.read().unwrap().keys_confirmed.get(host), Some(true))
true
} else {
false
}
} }
pub fn set_host_key_confirmed(host: &str, v: bool) { pub fn set_host_key_confirmed(host: &str, v: bool) {
@ -695,7 +688,7 @@ impl Config {
config.store(); config.store();
} }
pub fn get_key_pair() -> (Vec<u8>, Vec<u8>) { pub fn get_key_pair() -> KeyPair {
// lock here to make sure no gen_keypair more than once // lock here to make sure no gen_keypair more than once
// no use of CONFIG directly here to ensure no recursive calling in Config::load because of password dec which calling this function // no use of CONFIG directly here to ensure no recursive calling in Config::load because of password dec which calling this function
let mut lock = KEY_PAIR.lock().unwrap(); let mut lock = KEY_PAIR.lock().unwrap();
@ -714,7 +707,7 @@ impl Config {
}); });
} }
*lock = Some(config.key_pair.clone()); *lock = Some(config.key_pair.clone());
return config.key_pair; config.key_pair
} }
pub fn get_id() -> String { pub fn get_id() -> String {
@ -849,7 +842,7 @@ impl Config {
let ext = path.extension(); let ext = path.extension();
if let Some(ext) = ext { if let Some(ext) = ext {
let ext = format!("{}.toml", ext.to_string_lossy()); let ext = format!("{}.toml", ext.to_string_lossy());
path.with_extension(&ext) path.with_extension(ext)
} else { } else {
path.with_extension("toml") path.with_extension("toml")
} }
@ -861,7 +854,7 @@ const PEERS: &str = "peers";
impl PeerConfig { impl PeerConfig {
pub fn load(id: &str) -> PeerConfig { pub fn load(id: &str) -> PeerConfig {
let _lock = CONFIG.read().unwrap(); let _lock = CONFIG.read().unwrap();
match confy::load_path(&Self::path(id)) { match confy::load_path(Self::path(id)) {
Ok(config) => { Ok(config) => {
let mut config: PeerConfig = config; let mut config: PeerConfig = config;
let mut store = false; let mut store = false;
@ -869,16 +862,16 @@ impl PeerConfig {
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
config.password = password; config.password = password;
store = store || store2; store = store || store2;
config.options.get_mut("rdp_password").map(|v| { if let Some(v) = config.options.get_mut("rdp_password") {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = password; *v = password;
store = store || store2; store = store || store2;
}); }
config.options.get_mut("os-password").map(|v| { if let Some(v) = config.options.get_mut("os-password") {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = password; *v = password;
store = store || store2; store = store || store2;
}); }
if store { if store {
config.store(id); config.store(id);
} }
@ -895,34 +888,29 @@ impl PeerConfig {
let _lock = CONFIG.read().unwrap(); let _lock = CONFIG.read().unwrap();
let mut config = self.clone(); let mut config = self.clone();
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
config if let Some(v) = config.options.get_mut("rdp_password") {
.options *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
.get_mut("rdp_password") }
.map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)); if let Some(v) = config.options.get_mut("os-password") {
config *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
.options };
.get_mut("os-password")
.map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION));
if let Err(err) = store_path(Self::path(id), config) { if let Err(err) = store_path(Self::path(id), config) {
log::error!("Failed to store config: {}", err); log::error!("Failed to store config: {}", err);
} }
} }
pub fn remove(id: &str) { pub fn remove(id: &str) {
fs::remove_file(&Self::path(id)).ok(); fs::remove_file(Self::path(id)).ok();
} }
fn path(id: &str) -> PathBuf { fn path(id: &str) -> PathBuf {
let id_encoded: String;
//If the id contains invalid chars, encode it //If the id contains invalid chars, encode it
let forbidden_paths = Regex::new(r".*[<>:/\\|\?\*].*").unwrap(); let forbidden_paths = Regex::new(r".*[<>:/\\|\?\*].*").unwrap();
if forbidden_paths.is_match(id) { let id_encoded = if forbidden_paths.is_match(id) {
id_encoded = "base64_".to_string() + base64::encode(id, base64::Variant::Original).as_str()
"base64_".to_string() + base64::encode(id, base64::Variant::Original).as_str();
} else { } else {
id_encoded = id.to_string(); id.to_string()
} };
let path: PathBuf = [PEERS, id_encoded.as_str()].iter().collect(); let path: PathBuf = [PEERS, id_encoded.as_str()].iter().collect();
Config::with_extension(Config::path(path)) Config::with_extension(Config::path(path))
} }
@ -940,26 +928,24 @@ impl PeerConfig {
&& p.extension().map(|p| p.to_str().unwrap_or("")) == Some("toml") && p.extension().map(|p| p.to_str().unwrap_or("")) == Some("toml")
}) })
.map(|p| { .map(|p| {
let t = crate::get_modified_time(&p); let t = crate::get_modified_time(p);
let id = p let id = p
.file_stem() .file_stem()
.map(|p| p.to_str().unwrap_or("")) .map(|p| p.to_str().unwrap_or(""))
.unwrap_or("") .unwrap_or("")
.to_owned(); .to_owned();
let id_decoded_string: String; let id_decoded_string = if id.starts_with("base64_") && id.len() != 7 {
if id.starts_with("base64_") && id.len() != 7 {
let id_decoded = base64::decode(&id[7..], base64::Variant::Original) let id_decoded = base64::decode(&id[7..], base64::Variant::Original)
.unwrap_or(Vec::new()); .unwrap_or_default();
id_decoded_string = String::from_utf8_lossy(&id_decoded).as_ref().to_owned()
String::from_utf8_lossy(&id_decoded).as_ref().to_owned();
} else { } else {
id_decoded_string = id; id
} };
let c = PeerConfig::load(&id_decoded_string); let c = PeerConfig::load(&id_decoded_string);
if c.info.platform.is_empty() { if c.info.platform.is_empty() {
fs::remove_file(&p).ok(); fs::remove_file(p).ok();
} }
(id_decoded_string, t, c) (id_decoded_string, t, c)
}) })
@ -1149,7 +1135,7 @@ pub struct LanPeers {
impl LanPeers { impl LanPeers {
pub fn load() -> LanPeers { pub fn load() -> LanPeers {
let _lock = CONFIG.read().unwrap(); let _lock = CONFIG.read().unwrap();
match confy::load_path(&Config::file_("_lan_peers")) { match confy::load_path(Config::file_("_lan_peers")) {
Ok(peers) => peers, Ok(peers) => peers,
Err(err) => { Err(err) => {
log::error!("Failed to load lan peers: {}", err); log::error!("Failed to load lan peers: {}", err);
@ -1158,9 +1144,9 @@ impl LanPeers {
} }
} }
pub fn store(peers: &Vec<DiscoveryPeer>) { pub fn store(peers: &[DiscoveryPeer]) {
let f = LanPeers { let f = LanPeers {
peers: peers.clone(), peers: peers.to_owned(),
}; };
if let Err(err) = store_path(Config::file_("_lan_peers"), f) { if let Err(err) = store_path(Config::file_("_lan_peers"), f) {
log::error!("Failed to store lan peers: {}", err); log::error!("Failed to store lan peers: {}", err);

View File

@ -13,13 +13,13 @@ use crate::{
config::{Config, COMPRESS_LEVEL}, config::{Config, COMPRESS_LEVEL},
}; };
pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirectory> { pub fn read_dir(path: &Path, include_hidden: bool) -> ResultType<FileDirectory> {
let mut dir = FileDirectory { let mut dir = FileDirectory {
path: get_string(&path), path: get_string(path),
..Default::default() ..Default::default()
}; };
#[cfg(windows)] #[cfg(windows)]
if "/" == &get_string(&path) { if "/" == &get_string(path) {
let drives = unsafe { winapi::um::fileapi::GetLogicalDrives() }; let drives = unsafe { winapi::um::fileapi::GetLogicalDrives() };
for i in 0..32 { for i in 0..32 {
if drives & (1 << i) != 0 { if drives & (1 << i) != 0 {
@ -36,74 +36,70 @@ pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirector
} }
return Ok(dir); return Ok(dir);
} }
for entry in path.read_dir()? { for entry in path.read_dir()?.flatten() {
if let Ok(entry) = entry { let p = entry.path();
let p = entry.path(); let name = p
let name = p .file_name()
.file_name() .map(|p| p.to_str().unwrap_or(""))
.map(|p| p.to_str().unwrap_or("")) .unwrap_or("")
.unwrap_or("") .to_owned();
.to_owned(); if name.is_empty() {
if name.is_empty() { continue;
continue;
}
let mut is_hidden = false;
let meta;
if let Ok(tmp) = std::fs::symlink_metadata(&p) {
meta = tmp;
} else {
continue;
}
// docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
#[cfg(windows)]
if meta.file_attributes() & 0x2 != 0 {
is_hidden = true;
}
#[cfg(not(windows))]
if name.find('.').unwrap_or(usize::MAX) == 0 {
is_hidden = true;
}
if is_hidden && !include_hidden {
continue;
}
let (entry_type, size) = {
if p.is_dir() {
if meta.file_type().is_symlink() {
(FileType::DirLink.into(), 0)
} else {
(FileType::Dir.into(), 0)
}
} else {
if meta.file_type().is_symlink() {
(FileType::FileLink.into(), 0)
} else {
(FileType::File.into(), meta.len())
}
}
};
let modified_time = meta
.modified()
.map(|x| {
x.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map(|x| x.as_secs())
.unwrap_or(0)
})
.unwrap_or(0) as u64;
dir.entries.push(FileEntry {
name: get_file_name(&p),
entry_type,
is_hidden,
size,
modified_time,
..Default::default()
});
} }
let mut is_hidden = false;
let meta;
if let Ok(tmp) = std::fs::symlink_metadata(&p) {
meta = tmp;
} else {
continue;
}
// docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
#[cfg(windows)]
if meta.file_attributes() & 0x2 != 0 {
is_hidden = true;
}
#[cfg(not(windows))]
if name.find('.').unwrap_or(usize::MAX) == 0 {
is_hidden = true;
}
if is_hidden && !include_hidden {
continue;
}
let (entry_type, size) = {
if p.is_dir() {
if meta.file_type().is_symlink() {
(FileType::DirLink.into(), 0)
} else {
(FileType::Dir.into(), 0)
}
} else if meta.file_type().is_symlink() {
(FileType::FileLink.into(), 0)
} else {
(FileType::File.into(), meta.len())
}
};
let modified_time = meta
.modified()
.map(|x| {
x.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map(|x| x.as_secs())
.unwrap_or(0)
})
.unwrap_or(0);
dir.entries.push(FileEntry {
name: get_file_name(&p),
entry_type,
is_hidden,
size,
modified_time,
..Default::default()
});
} }
Ok(dir) Ok(dir)
} }
#[inline] #[inline]
pub fn get_file_name(p: &PathBuf) -> String { pub fn get_file_name(p: &Path) -> String {
p.file_name() p.file_name()
.map(|p| p.to_str().unwrap_or("")) .map(|p| p.to_str().unwrap_or(""))
.unwrap_or("") .unwrap_or("")
@ -111,7 +107,7 @@ pub fn get_file_name(p: &PathBuf) -> String {
} }
#[inline] #[inline]
pub fn get_string(path: &PathBuf) -> String { pub fn get_string(path: &Path) -> String {
path.to_str().unwrap_or("").to_owned() path.to_str().unwrap_or("").to_owned()
} }
@ -127,14 +123,14 @@ pub fn get_home_as_string() -> String {
fn read_dir_recursive( fn read_dir_recursive(
path: &PathBuf, path: &PathBuf,
prefix: &PathBuf, prefix: &Path,
include_hidden: bool, include_hidden: bool,
) -> ResultType<Vec<FileEntry>> { ) -> ResultType<Vec<FileEntry>> {
let mut files = Vec::new(); let mut files = Vec::new();
if path.is_dir() { if path.is_dir() {
// to-do: symbol link handling, cp the link rather than the content // to-do: symbol link handling, cp the link rather than the content
// to-do: file mode, for unix // to-do: file mode, for unix
let fd = read_dir(&path, include_hidden)?; let fd = read_dir(path, include_hidden)?;
for entry in fd.entries.iter() { for entry in fd.entries.iter() {
match entry.entry_type.enum_value() { match entry.entry_type.enum_value() {
Ok(FileType::File) => { Ok(FileType::File) => {
@ -158,7 +154,7 @@ fn read_dir_recursive(
} }
Ok(files) Ok(files)
} else if path.is_file() { } else if path.is_file() {
let (size, modified_time) = if let Ok(meta) = std::fs::metadata(&path) { let (size, modified_time) = if let Ok(meta) = std::fs::metadata(path) {
( (
meta.len(), meta.len(),
meta.modified() meta.modified()
@ -167,7 +163,7 @@ fn read_dir_recursive(
.map(|x| x.as_secs()) .map(|x| x.as_secs())
.unwrap_or(0) .unwrap_or(0)
}) })
.unwrap_or(0) as u64, .unwrap_or(0),
) )
} else { } else {
(0, 0) (0, 0)
@ -249,7 +245,7 @@ pub struct RemoveJobMeta {
#[inline] #[inline]
fn get_ext(name: &str) -> &str { fn get_ext(name: &str) -> &str {
if let Some(i) = name.rfind(".") { if let Some(i) = name.rfind('.') {
return &name[i + 1..]; return &name[i + 1..];
} }
"" ""
@ -270,6 +266,7 @@ fn is_compressed_file(name: &str) -> bool {
} }
impl TransferJob { impl TransferJob {
#[allow(clippy::too_many_arguments)]
pub fn new_write( pub fn new_write(
id: i32, id: i32,
remote: String, remote: String,
@ -281,7 +278,7 @@ impl TransferJob {
enable_overwrite_detection: bool, enable_overwrite_detection: bool,
) -> Self { ) -> Self {
log::info!("new write {}", path); log::info!("new write {}", path);
let total_size = files.iter().map(|x| x.size as u64).sum(); let total_size = files.iter().map(|x| x.size).sum();
Self { Self {
id, id,
remote, remote,
@ -307,7 +304,7 @@ impl TransferJob {
) -> ResultType<Self> { ) -> ResultType<Self> {
log::info!("new read {}", path); log::info!("new read {}", path);
let files = get_recursive_files(&path, show_hidden)?; let files = get_recursive_files(&path, show_hidden)?;
let total_size = files.iter().map(|x| x.size as u64).sum(); let total_size = files.iter().map(|x| x.size).sum();
Ok(Self { Ok(Self {
id, id,
remote, remote,
@ -363,7 +360,7 @@ impl TransferJob {
let entry = &self.files[file_num]; let entry = &self.files[file_num];
let path = self.join(&entry.name); let path = self.join(&entry.name);
let download_path = format!("{}.download", get_string(&path)); let download_path = format!("{}.download", get_string(&path));
std::fs::rename(&download_path, &path).ok(); std::fs::rename(download_path, &path).ok();
filetime::set_file_mtime( filetime::set_file_mtime(
&path, &path,
filetime::FileTime::from_unix_time(entry.modified_time as _, 0), filetime::FileTime::from_unix_time(entry.modified_time as _, 0),
@ -378,7 +375,7 @@ impl TransferJob {
let entry = &self.files[file_num]; let entry = &self.files[file_num];
let path = self.join(&entry.name); let path = self.join(&entry.name);
let download_path = format!("{}.download", get_string(&path)); let download_path = format!("{}.download", get_string(&path));
std::fs::remove_file(&download_path).ok(); std::fs::remove_file(download_path).ok();
} }
} }
@ -433,7 +430,7 @@ impl TransferJob {
} }
let name = &self.files[file_num].name; let name = &self.files[file_num].name;
if self.file.is_none() { if self.file.is_none() {
match File::open(self.join(&name)).await { match File::open(self.join(name)).await {
Ok(file) => { Ok(file) => {
self.file = Some(file); self.file = Some(file);
self.file_confirmed = false; self.file_confirmed = false;
@ -447,20 +444,15 @@ impl TransferJob {
} }
} }
} }
if self.enable_overwrite_detection { if self.enable_overwrite_detection && !self.file_confirmed() {
if !self.file_confirmed() { if !self.file_is_waiting() {
if !self.file_is_waiting() { self.send_current_digest(stream).await?;
self.send_current_digest(stream).await?; self.set_file_is_waiting(true);
self.set_file_is_waiting(true);
}
return Ok(None);
} }
return Ok(None);
} }
const BUF_SIZE: usize = 128 * 1024; const BUF_SIZE: usize = 128 * 1024;
let mut buf: Vec<u8> = Vec::with_capacity(BUF_SIZE); let mut buf: Vec<u8> = vec![0; BUF_SIZE];
unsafe {
buf.set_len(BUF_SIZE);
}
let mut compressed = false; let mut compressed = false;
let mut offset: usize = 0; let mut offset: usize = 0;
loop { loop {
@ -582,10 +574,7 @@ impl TransferJob {
#[inline] #[inline]
pub fn job_completed(&self) -> bool { pub fn job_completed(&self) -> bool {
// has no error, Condition 2 // has no error, Condition 2
if !self.enable_overwrite_detection || (!self.file_confirmed && !self.file_is_waiting) { !self.enable_overwrite_detection || (!self.file_confirmed && !self.file_is_waiting)
return true;
}
return false;
} }
/// Get job error message, useful for getting status when job had finished /// Get job error message, useful for getting status when job had finished
@ -660,7 +649,7 @@ pub fn new_dir(id: i32, path: String, files: Vec<FileEntry>) -> Message {
resp.set_dir(FileDirectory { resp.set_dir(FileDirectory {
id, id,
path, path,
entries: files.into(), entries: files,
..Default::default() ..Default::default()
}); });
let mut msg_out = Message::new(); let mut msg_out = Message::new();
@ -692,7 +681,7 @@ pub fn new_receive(id: i32, path: String, file_num: i32, files: Vec<FileEntry>)
action.set_receive(FileTransferReceiveRequest { action.set_receive(FileTransferReceiveRequest {
id, id,
path, path,
files: files.into(), files,
file_num, file_num,
..Default::default() ..Default::default()
}); });
@ -736,8 +725,8 @@ pub fn remove_job(id: i32, jobs: &mut Vec<TransferJob>) {
} }
#[inline] #[inline]
pub fn get_job(id: i32, jobs: &mut Vec<TransferJob>) -> Option<&mut TransferJob> { pub fn get_job(id: i32, jobs: &mut [TransferJob]) -> Option<&mut TransferJob> {
jobs.iter_mut().filter(|x| x.id() == id).next() jobs.iter_mut().find(|x| x.id() == id)
} }
pub async fn handle_read_jobs( pub async fn handle_read_jobs(
@ -789,7 +778,7 @@ pub fn remove_all_empty_dir(path: &PathBuf) -> ResultType<()> {
remove_all_empty_dir(&path.join(&entry.name)).ok(); remove_all_empty_dir(&path.join(&entry.name)).ok();
} }
Ok(FileType::DirLink) | Ok(FileType::FileLink) => { Ok(FileType::DirLink) | Ok(FileType::FileLink) => {
std::fs::remove_file(&path.join(&entry.name)).ok(); std::fs::remove_file(path.join(&entry.name)).ok();
} }
_ => {} _ => {}
} }
@ -813,7 +802,7 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
#[inline] #[inline]
pub fn transform_windows_path(entries: &mut Vec<FileEntry>) { pub fn transform_windows_path(entries: &mut Vec<FileEntry>) {
for entry in entries { for entry in entries {
entry.name = entry.name.replace("\\", "/"); entry.name = entry.name.replace('\\', "/");
} }
} }

View File

@ -96,8 +96,24 @@ pub type ResultType<F, E = anyhow::Error> = anyhow::Result<F, E>;
pub struct AddrMangle(); pub struct AddrMangle();
#[inline]
pub fn try_into_v4(addr: SocketAddr) -> SocketAddr {
match addr {
SocketAddr::V6(v6) if !addr.ip().is_loopback() => {
if let Some(v4) = v6.ip().to_ipv4() {
SocketAddr::new(IpAddr::V4(v4), addr.port())
} else {
addr
}
}
_ => addr,
}
}
impl AddrMangle { impl AddrMangle {
pub fn encode(addr: SocketAddr) -> Vec<u8> { pub fn encode(addr: SocketAddr) -> Vec<u8> {
// not work with [:1]:<port>
let addr = try_into_v4(addr);
match addr { match addr {
SocketAddr::V4(addr_v4) => { SocketAddr::V4(addr_v4) => {
let tm = (SystemTime::now() let tm = (SystemTime::now()
@ -129,22 +145,20 @@ impl AddrMangle {
} }
pub fn decode(bytes: &[u8]) -> SocketAddr { pub fn decode(bytes: &[u8]) -> SocketAddr {
use std::convert::TryInto;
if bytes.len() > 16 { if bytes.len() > 16 {
if bytes.len() != 18 { if bytes.len() != 18 {
return Config::get_any_listen_addr(false); return Config::get_any_listen_addr(false);
} }
#[allow(invalid_value)] let tmp: [u8; 2] = bytes[16..].try_into().unwrap();
let mut tmp: [u8; 2] = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
tmp.copy_from_slice(&bytes[16..]);
let port = u16::from_le_bytes(tmp); let port = u16::from_le_bytes(tmp);
#[allow(invalid_value)] let tmp: [u8; 16] = bytes[..16].try_into().unwrap();
let mut tmp: [u8; 16] = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
tmp.copy_from_slice(&bytes[..16]);
let ip = std::net::Ipv6Addr::from(tmp); let ip = std::net::Ipv6Addr::from(tmp);
return SocketAddr::new(IpAddr::V6(ip), port); return SocketAddr::new(IpAddr::V6(ip), port);
} }
let mut padded = [0u8; 16]; let mut padded = [0u8; 16];
padded[..bytes.len()].copy_from_slice(&bytes); padded[..bytes.len()].copy_from_slice(bytes);
let number = u128::from_le_bytes(padded); let number = u128::from_le_bytes(padded);
let tm = (number >> 17) & (u32::max_value() as u128); let tm = (number >> 17) & (u32::max_value() as u128);
let ip = (((number >> 49) - tm) as u32).to_le_bytes(); let ip = (((number >> 49) - tm) as u32).to_le_bytes();
@ -158,21 +172,9 @@ impl AddrMangle {
pub fn get_version_from_url(url: &str) -> String { pub fn get_version_from_url(url: &str) -> String {
let n = url.chars().count(); let n = url.chars().count();
let a = url let a = url.chars().rev().position(|x| x == '-');
.chars()
.rev()
.enumerate()
.filter(|(_, x)| x == &'-')
.next()
.map(|(i, _)| i);
if let Some(a) = a { if let Some(a) = a {
let b = url let b = url.chars().rev().position(|x| x == '.');
.chars()
.rev()
.enumerate()
.filter(|(_, x)| x == &'.')
.next()
.map(|(i, _)| i);
if let Some(b) = b { if let Some(b) = b {
if a > b { if a > b {
if url if url
@ -195,22 +197,30 @@ pub fn get_version_from_url(url: &str) -> String {
} }
pub fn gen_version() { pub fn gen_version() {
if Ok("release".to_owned()) != std::env::var("PROFILE") {
return;
}
println!("cargo:rerun-if-changed=Cargo.toml");
use std::io::prelude::*; use std::io::prelude::*;
let mut file = File::create("./src/version.rs").unwrap(); let mut file = File::create("./src/version.rs").unwrap();
for line in read_lines("Cargo.toml").unwrap() { for line in read_lines("Cargo.toml").unwrap().flatten() {
if let Ok(line) = line { let ab: Vec<&str> = line.split('=').map(|x| x.trim()).collect();
let ab: Vec<&str> = line.split("=").map(|x| x.trim()).collect(); if ab.len() == 2 && ab[0] == "version" {
if ab.len() == 2 && ab[0] == "version" { file.write_all(format!("pub const VERSION: &str = {};\n", ab[1]).as_bytes())
file.write_all(format!("pub const VERSION: &str = {};\n", ab[1]).as_bytes()) .ok();
.ok(); break;
break;
}
} }
} }
// generate build date // generate build date
let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M")); let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M"));
file.write_all(format!("pub const BUILD_DATE: &str = \"{}\";", build_date).as_bytes()) file.write_all(
.ok(); format!(
"#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{}\";",
build_date
)
.as_bytes(),
)
.ok();
file.sync_all().ok(); file.sync_all().ok();
} }
@ -230,20 +240,20 @@ pub fn is_valid_custom_id(id: &str) -> bool {
pub fn get_version_number(v: &str) -> i64 { pub fn get_version_number(v: &str) -> i64 {
let mut n = 0; let mut n = 0;
for x in v.split(".") { for x in v.split('.') {
n = n * 1000 + x.parse::<i64>().unwrap_or(0); n = n * 1000 + x.parse::<i64>().unwrap_or(0);
} }
n n
} }
pub fn get_modified_time(path: &std::path::Path) -> SystemTime { pub fn get_modified_time(path: &std::path::Path) -> SystemTime {
std::fs::metadata(&path) std::fs::metadata(path)
.map(|m| m.modified().unwrap_or(UNIX_EPOCH)) .map(|m| m.modified().unwrap_or(UNIX_EPOCH))
.unwrap_or(UNIX_EPOCH) .unwrap_or(UNIX_EPOCH)
} }
pub fn get_created_time(path: &std::path::Path) -> SystemTime { pub fn get_created_time(path: &std::path::Path) -> SystemTime {
std::fs::metadata(&path) std::fs::metadata(path)
.map(|m| m.created().unwrap_or(UNIX_EPOCH)) .map(|m| m.created().unwrap_or(UNIX_EPOCH))
.unwrap_or(UNIX_EPOCH) .unwrap_or(UNIX_EPOCH)
} }
@ -276,32 +286,6 @@ pub fn get_time() -> i64 {
.unwrap_or(0) as _ .unwrap_or(0) as _
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mangle() {
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 16, 32), 21116));
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
let addr = "[2001:db8::1]:8080".parse::<SocketAddr>().unwrap();
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
let addr = "[2001:db8:ff::1111]:80".parse::<SocketAddr>().unwrap();
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
}
#[test]
fn test_allow_err() {
allow_err!(Err("test err") as Result<(), &str>);
allow_err!(
Err("test err with msg") as Result<(), &str>,
"prompt {}",
"failed"
);
}
}
#[inline] #[inline]
pub fn is_ipv4_str(id: &str) -> bool { pub fn is_ipv4_str(id: &str) -> bool {
regex::Regex::new(r"^\d+\.\d+\.\d+\.\d+(:\d+)?$") regex::Regex::new(r"^\d+\.\d+\.\d+\.\d+(:\d+)?$")
@ -334,9 +318,31 @@ pub fn is_domain_port_str(id: &str) -> bool {
} }
#[cfg(test)] #[cfg(test)]
mod test_lib { mod test {
use super::*; use super::*;
#[test]
fn test_mangle() {
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 16, 32), 21116));
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
let addr = "[2001:db8::1]:8080".parse::<SocketAddr>().unwrap();
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
let addr = "[2001:db8:ff::1111]:80".parse::<SocketAddr>().unwrap();
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
}
#[test]
fn test_allow_err() {
allow_err!(Err("test err") as Result<(), &str>);
allow_err!(
Err("test err with msg") as Result<(), &str>,
"prompt {}",
"failed"
);
}
#[test] #[test]
fn test_ipv6() { fn test_ipv6() {
assert_eq!(is_ipv6_str("1:2:3"), true); assert_eq!(is_ipv6_str("1:2:3"), true);
@ -373,4 +379,20 @@ mod test_lib {
assert_eq!(is_domain_port_str("test.com:0"), true); assert_eq!(is_domain_port_str("test.com:0"), true);
assert_eq!(is_domain_port_str("test.com:98989"), true); assert_eq!(is_domain_port_str("test.com:98989"), true);
} }
#[test]
fn test_mangle2() {
let addr = "[::ffff:127.0.0.1]:8080".parse().unwrap();
let addr_v4 = "127.0.0.1:8080".parse().unwrap();
assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr)), addr_v4);
assert_eq!(
AddrMangle::decode(&AddrMangle::encode("[::127.0.0.1]:8080".parse().unwrap())),
addr_v4
);
assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v4)), addr_v4);
let addr_v6 = "[ef::fe]:8080".parse().unwrap();
assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v6)), addr_v6);
let addr_v6 = "[::1]:8080".parse().unwrap();
assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v6)), addr_v6);
}
} }

View File

@ -104,7 +104,7 @@ pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool,
if s.len() > VERSION_LEN { if s.len() > VERSION_LEN {
let version = &s[..VERSION_LEN]; let version = &s[..VERSION_LEN];
if version == "00" { if version == "00" {
if let Ok(v) = decrypt(&s[VERSION_LEN..].as_bytes()) { if let Ok(v) = decrypt(s[VERSION_LEN..].as_bytes()) {
return ( return (
String::from_utf8_lossy(&v).to_string(), String::from_utf8_lossy(&v).to_string(),
true, true,
@ -149,7 +149,7 @@ pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec<u8>, boo
} }
fn encrypt(v: &[u8]) -> Result<String, ()> { fn encrypt(v: &[u8]) -> Result<String, ()> {
if v.len() > 0 { if !v.is_empty() {
symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original)) symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
} else { } else {
Err(()) Err(())
@ -157,7 +157,7 @@ fn encrypt(v: &[u8]) -> Result<String, ()> {
} }
fn decrypt(v: &[u8]) -> Result<Vec<u8>, ()> { fn decrypt(v: &[u8]) -> Result<Vec<u8>, ()> {
if v.len() > 0 { if !v.is_empty() {
base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false)) base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false))
} else { } else {
Err(()) Err(())

View File

@ -32,7 +32,7 @@ pub fn get_display_server() -> String {
// loginctl has not given the expected output. try something else. // loginctl has not given the expected output. try something else.
if let Ok(sid) = std::env::var("XDG_SESSION_ID") { if let Ok(sid) = std::env::var("XDG_SESSION_ID") {
// could also execute "cat /proc/self/sessionid" // could also execute "cat /proc/self/sessionid"
session = sid.to_owned(); session = sid;
} }
if session.is_empty() { if session.is_empty() {
session = run_cmds("cat /proc/self/sessionid".to_owned()).unwrap_or_default(); session = run_cmds("cat /proc/self/sessionid".to_owned()).unwrap_or_default();
@ -63,7 +63,7 @@ fn get_display_server_of_session(session: &str) -> String {
if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty))
// And check if Xorg is running on that tty // And check if Xorg is running on that tty
{ {
if xorg_results.trim_end().to_string() != "" { if xorg_results.trim_end() != "" {
// If it is, manually return "x11", otherwise return tty // If it is, manually return "x11", otherwise return tty
return "x11".to_owned(); return "x11".to_owned();
} }
@ -88,7 +88,7 @@ pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
if let Ok(output) = run_loginctl(None) { if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.contains("seat0") { if line.contains("seat0") {
if let Some(sid) = line.split_whitespace().nth(0) { if let Some(sid) = line.split_whitespace().next() {
if is_active(sid) { if is_active(sid) {
return indices return indices
.into_iter() .into_iter()
@ -103,7 +103,7 @@ pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
if let Ok(output) = run_loginctl(None) { if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
if let Some(sid) = line.split_whitespace().nth(0) { if let Some(sid) = line.split_whitespace().next() {
let d = get_display_server_of_session(sid); let d = get_display_server_of_session(sid);
if is_active(sid) && d != "tty" { if is_active(sid) && d != "tty" {
return indices return indices

View File

@ -71,7 +71,7 @@ pub trait IsResolvedSocketAddr {
impl IsResolvedSocketAddr for SocketAddr { impl IsResolvedSocketAddr for SocketAddr {
fn resolve(&self) -> Option<&SocketAddr> { fn resolve(&self) -> Option<&SocketAddr> {
Some(&self) Some(self)
} }
} }
@ -120,12 +120,12 @@ pub async fn connect_tcp_local<
if let Some(target) = target.resolve() { if let Some(target) = target.resolve() {
if let Some(local) = local { if let Some(local) = local {
if local.is_ipv6() && target.is_ipv4() { if local.is_ipv6() && target.is_ipv4() {
let target = query_nip_io(&target).await?; let target = query_nip_io(target).await?;
return Ok(FramedStream::new(target, Some(local), ms_timeout).await?); return FramedStream::new(target, Some(local), ms_timeout).await;
} }
} }
} }
Ok(FramedStream::new(target, local, ms_timeout).await?) FramedStream::new(target, local, ms_timeout).await
} }
#[inline] #[inline]
@ -140,15 +140,14 @@ pub fn is_ipv4(target: &TargetAddr<'_>) -> bool {
pub async fn query_nip_io(addr: &SocketAddr) -> ResultType<SocketAddr> { pub async fn query_nip_io(addr: &SocketAddr) -> ResultType<SocketAddr> {
tokio::net::lookup_host(format!("{}.nip.io:{}", addr.ip(), addr.port())) tokio::net::lookup_host(format!("{}.nip.io:{}", addr.ip(), addr.port()))
.await? .await?
.filter(|x| x.is_ipv6()) .find(|x| x.is_ipv6())
.next()
.context("Failed to get ipv6 from nip.io") .context("Failed to get ipv6 from nip.io")
} }
#[inline] #[inline]
pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String { pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String {
if !ipv4 && crate::is_ipv4_str(&addr) { if !ipv4 && crate::is_ipv4_str(&addr) {
if let Some(ip) = addr.split(":").next() { if let Some(ip) = addr.split(':').next() {
return addr.replace(ip, &format!("{}.nip.io", ip)); return addr.replace(ip, &format!("{}.nip.io", ip));
} }
} }

View File

@ -1,4 +1,5 @@
use crate::{bail, bytes_codec::BytesCodec, ResultType}; use crate::{bail, bytes_codec::BytesCodec, ResultType};
use anyhow::Context as AnyhowCtx;
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use protobuf::Message; use protobuf::Message;
@ -209,7 +210,7 @@ impl FramedStream {
if let Some(Ok(bytes)) = res.as_mut() { if let Some(Ok(bytes)) = res.as_mut() {
key.2 += 1; key.2 += 1;
let nonce = Self::get_nonce(key.2); let nonce = Self::get_nonce(key.2);
match secretbox::open(&bytes, &nonce, &key.0) { match secretbox::open(bytes, &nonce, &key.0) {
Ok(res) => { Ok(res) => {
bytes.clear(); bytes.clear();
bytes.put_slice(&res); bytes.put_slice(&res);
@ -245,16 +246,17 @@ impl FramedStream {
const DEFAULT_BACKLOG: u32 = 128; const DEFAULT_BACKLOG: u32 = 128;
#[allow(clippy::never_loop)]
pub async fn new_listener<T: ToSocketAddrs>(addr: T, reuse: bool) -> ResultType<TcpListener> { pub async fn new_listener<T: ToSocketAddrs>(addr: T, reuse: bool) -> ResultType<TcpListener> {
if !reuse { if !reuse {
Ok(TcpListener::bind(addr).await?) Ok(TcpListener::bind(addr).await?)
} else { } else {
for addr in lookup_host(&addr).await? { let addr = lookup_host(&addr)
let socket = new_socket(addr, true)?; .await?
return Ok(socket.listen(DEFAULT_BACKLOG)?); .next()
} .context("could not resolve to any address")?;
bail!("could not resolve to any address"); new_socket(addr, true)?
.listen(DEFAULT_BACKLOG)
.map_err(anyhow::Error::msg)
} }
} }

View File

@ -1,11 +1,11 @@
use crate::{bail, ResultType}; use crate::ResultType;
use anyhow::anyhow; use anyhow::{anyhow, Context};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use protobuf::Message; use protobuf::Message;
use socket2::{Domain, Socket, Type}; use socket2::{Domain, Socket, Type};
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio::net::{ToSocketAddrs, UdpSocket}; use tokio::net::{lookup_host, ToSocketAddrs, UdpSocket};
use tokio_socks::{udp::Socks5UdpFramed, IntoTargetAddr, TargetAddr, ToProxyAddrs}; use tokio_socks::{udp::Socks5UdpFramed, IntoTargetAddr, TargetAddr, ToProxyAddrs};
use tokio_util::{codec::BytesCodec, udp::UdpFramed}; use tokio_util::{codec::BytesCodec, udp::UdpFramed};
@ -37,39 +37,31 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result<Socket,
addr, addr,
socket.recv_buffer_size() socket.recv_buffer_size()
); );
if addr.is_ipv6() && addr.ip().is_unspecified() && addr.port() > 0 {
socket.set_only_v6(false).ok();
}
socket.bind(&addr.into())?; socket.bind(&addr.into())?;
Ok(socket) Ok(socket)
} }
impl FramedSocket { impl FramedSocket {
pub async fn new<T: ToSocketAddrs>(addr: T) -> ResultType<Self> { pub async fn new<T: ToSocketAddrs>(addr: T) -> ResultType<Self> {
let socket = UdpSocket::bind(addr).await?; Self::new_reuse(addr, false, 0).await
Ok(Self::Direct(UdpFramed::new(socket, BytesCodec::new())))
} }
#[allow(clippy::never_loop)] pub async fn new_reuse<T: ToSocketAddrs>(
pub async fn new_reuse<T: std::net::ToSocketAddrs>(addr: T) -> ResultType<Self> {
for addr in addr.to_socket_addrs()? {
let socket = new_socket(addr, true, 0)?.into_udp_socket();
return Ok(Self::Direct(UdpFramed::new(
UdpSocket::from_std(socket)?,
BytesCodec::new(),
)));
}
bail!("could not resolve to any address");
}
pub async fn new_with_buf_size<T: std::net::ToSocketAddrs>(
addr: T, addr: T,
reuse: bool,
buf_size: usize, buf_size: usize,
) -> ResultType<Self> { ) -> ResultType<Self> {
for addr in addr.to_socket_addrs()? { let addr = lookup_host(&addr)
return Ok(Self::Direct(UdpFramed::new( .await?
UdpSocket::from_std(new_socket(addr, false, buf_size)?.into_udp_socket())?, .next()
BytesCodec::new(), .context("could not resolve to any address")?;
))); Ok(Self::Direct(UdpFramed::new(
} UdpSocket::from_std(new_socket(addr, reuse, buf_size)?.into_udp_socket())?,
bail!("could not resolve to any address"); BytesCodec::new(),
)))
} }
pub async fn new_proxy<'a, 't, P: ToProxyAddrs, T: ToSocketAddrs>( pub async fn new_proxy<'a, 't, P: ToProxyAddrs, T: ToSocketAddrs>(
@ -104,11 +96,12 @@ impl FramedSocket {
) -> ResultType<()> { ) -> ResultType<()> {
let addr = addr.into_target_addr()?.to_owned(); let addr = addr.into_target_addr()?.to_owned();
let send_data = Bytes::from(msg.write_to_bytes()?); let send_data = Bytes::from(msg.write_to_bytes()?);
let _ = match self { match self {
Self::Direct(f) => match addr { Self::Direct(f) => {
TargetAddr::Ip(addr) => f.send((send_data, addr)).await?, if let TargetAddr::Ip(addr) = addr {
_ => {} f.send((send_data, addr)).await?
}, }
}
Self::ProxySocks(f) => f.send((send_data, addr)).await?, Self::ProxySocks(f) => f.send((send_data, addr)).await?,
}; };
Ok(()) Ok(())
@ -123,11 +116,12 @@ impl FramedSocket {
) -> ResultType<()> { ) -> ResultType<()> {
let addr = addr.into_target_addr()?.to_owned(); let addr = addr.into_target_addr()?.to_owned();
let _ = match self { match self {
Self::Direct(f) => match addr { Self::Direct(f) => {
TargetAddr::Ip(addr) => f.send((Bytes::from(msg), addr)).await?, if let TargetAddr::Ip(addr) = addr {
_ => {} f.send((Bytes::from(msg), addr)).await?
}, }
}
Self::ProxySocks(f) => f.send((Bytes::from(msg), addr)).await?, Self::ProxySocks(f) => f.send((Bytes::from(msg), addr)).await?,
}; };
Ok(()) Ok(())
@ -165,12 +159,12 @@ impl FramedSocket {
} }
} }
pub fn is_ipv4(&self) -> bool { pub fn local_addr(&self) -> Option<SocketAddr> {
if let FramedSocket::Direct(x) = self { if let FramedSocket::Direct(x) = self {
if let Ok(v) = x.get_ref().local_addr() { if let Ok(v) = x.get_ref().local_addr() {
return v.is_ipv4(); return Some(v);
} }
} }
true None
} }
} }