no password required for file transfer action in remote control menu (#9731)

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages
2024-10-24 17:20:48 +08:00
committed by GitHub
parent 7a3e1fe648
commit 445e9ac285
16 changed files with 222 additions and 52 deletions

View File

@@ -11,6 +11,7 @@ use crossbeam_queue::ArrayQueue;
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
#[cfg(not(any(target_os = "android", target_os = "linux")))]
use ringbuf::{ring_buffer::RbBase, Rb};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{
collections::HashMap,
@@ -1274,7 +1275,7 @@ impl VideoHandler {
}
// The source of sent password
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
enum PasswordSource {
PersonalAb(Vec<u8>),
SharedAb(String),
@@ -1320,6 +1321,13 @@ impl PasswordSource {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct ConnToken {
password: Vec<u8>,
password_source: PasswordSource,
session_id: u64,
}
/// Login config handler for [`Client`].
#[derive(Default)]
pub struct LoginConfigHandler {
@@ -1376,6 +1384,7 @@ impl LoginConfigHandler {
mut force_relay: bool,
adapter_luid: Option<i64>,
shared_password: Option<String>,
conn_token: Option<String>,
) {
let mut id = id;
if id.contains("@") {
@@ -1419,10 +1428,22 @@ impl LoginConfigHandler {
let config = self.load_config();
self.remember = !config.password.is_empty();
self.config = config;
let mut sid = rand::random();
let conn_token = conn_token
.map(|x| serde_json::from_str::<ConnToken>(&x).ok())
.flatten();
let mut sid = 0;
if let Some(token) = conn_token {
sid = token.session_id;
self.password = token.password; // use as last password
self.password_source = token.password_source;
}
if sid == 0 {
// you won the lottery
sid = 1;
sid = rand::random();
if sid == 0 {
// you won the lottery
sid = 1;
}
}
self.session_id = sid;
self.supported_encoding = Default::default();
@@ -2223,6 +2244,18 @@ impl LoginConfigHandler {
msg_out.set_misc(misc);
msg_out
}
pub fn get_conn_token(&self) -> Option<String> {
if self.password.is_empty() {
return None;
}
serde_json::to_string(&ConnToken {
password: self.password.clone(),
password_source: self.password_source.clone(),
session_id: self.session_id,
})
.ok()
}
}
/// Media data.

View File

@@ -1126,6 +1126,7 @@ pub fn session_add(
force_relay: bool,
password: String,
is_shared_password: bool,
conn_token: Option<String>,
) -> ResultType<FlutterSession> {
let conn_type = if is_file_transfer {
ConnType::FILE_TRANSFER
@@ -1180,6 +1181,7 @@ pub fn session_add(
force_relay,
get_adapter_luid(),
shared_password,
conn_token,
);
let session = Arc::new(session.clone());

View File

@@ -121,6 +121,7 @@ pub fn session_add_sync(
force_relay: bool,
password: String,
is_shared_password: bool,
conn_token: Option<String>,
) -> SyncReturn<String> {
if let Err(e) = session_add(
&session_id,
@@ -132,6 +133,7 @@ pub fn session_add_sync(
force_relay,
password,
is_shared_password,
conn_token,
) {
SyncReturn(format!("Failed to add session with id {}, {}", &id, e))
} else {
@@ -1341,6 +1343,14 @@ pub fn session_close_voice_call(session_id: SessionID) {
}
}
pub fn session_get_conn_token(session_id: SessionID) -> SyncReturn<Option<String>> {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
SyncReturn(session.get_conn_token())
} else {
SyncReturn(None)
}
}
pub fn cm_handle_incoming_voice_call(id: i32, accept: bool) {
crate::ui_cm_interface::handle_incoming_voice_call(id, accept);
}

View File

@@ -64,9 +64,9 @@ pub type Sender = mpsc::UnboundedSender<(Instant, Arc<Message>)>;
lazy_static::lazy_static! {
static ref LOGIN_FAILURES: [Arc::<Mutex<HashMap<String, (i32, i32, i32)>>>; 2] = Default::default();
static ref SESSIONS: Arc::<Mutex<HashMap<String, Session>>> = Default::default();
static ref SESSIONS: Arc::<Mutex<HashMap<SessionKey, Session>>> = Default::default();
static ref ALIVE_CONNS: Arc::<Mutex<Vec<i32>>> = Default::default();
pub static ref AUTHED_CONNS: Arc::<Mutex<Vec<(i32, AuthConnType)>>> = Default::default();
pub static ref AUTHED_CONNS: Arc::<Mutex<Vec<(i32, AuthConnType, SessionKey)>>> = Default::default();
static ref SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
static ref WAKELOCK_SENDER: Arc::<Mutex<std::sync::mpsc::Sender<(usize, usize)>>> = Arc::new(Mutex::new(start_wakelock_thread()));
}
@@ -140,13 +140,20 @@ enum MessageInput {
BlockOffPlugin(String),
}
#[derive(Clone, Debug)]
struct Session {
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct SessionKey {
peer_id: String,
name: String,
session_id: u64,
}
#[derive(Clone, Debug)]
struct Session {
last_recv_time: Arc<Mutex<Instant>>,
random_password: String,
tfa: bool,
conn_type: AuthConnType,
conn_id: i32,
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -1131,6 +1138,7 @@ impl Connection {
self.authed_conn_id = Some(self::raii::AuthedConnID::new(
self.inner.id(),
auth_conn_type,
self.session_key(),
));
self.post_conn_audit(
json!({"peer": ((&self.lr.my_id, &self.lr.my_name)), "type": conn_type}),
@@ -1541,14 +1549,14 @@ impl Connection {
if password::temporary_enabled() {
let password = password::temporary_password();
if self.validate_one_password(password.clone()) {
SESSIONS.lock().unwrap().insert(
self.lr.my_id.clone(),
raii::AuthedConnID::insert_session(
self.session_key(),
Session {
name: self.lr.my_name.clone(),
session_id: self.lr.session_id,
last_recv_time: self.last_recv_time.clone(),
random_password: password,
tfa: false,
conn_type: self.conn_type(),
conn_id: self.inner.id(),
},
);
return true;
@@ -1570,21 +1578,19 @@ impl Connection {
let session = SESSIONS
.lock()
.unwrap()
.get(&self.lr.my_id)
.get(&self.session_key())
.map(|s| s.to_owned());
// last_recv_time is a mutex variable shared with connection, can be updated lively.
if let Some(mut session) = session {
if session.name == self.lr.my_name
&& session.session_id == self.lr.session_id
&& !self.lr.password.is_empty()
if !self.lr.password.is_empty()
&& (tfa && session.tfa
|| !tfa && self.validate_one_password(session.random_password.clone()))
{
session.last_recv_time = self.last_recv_time.clone();
SESSIONS
.lock()
.unwrap()
.insert(self.lr.my_id.clone(), session);
session.conn_id = self.inner.id();
session.conn_type = self.conn_type();
raii::AuthedConnID::insert_session(self.session_key(), session);
log::info!("is recent session");
return true;
}
}
@@ -1844,23 +1850,22 @@ impl Connection {
let session = SESSIONS
.lock()
.unwrap()
.get(&self.lr.my_id)
.get(&self.session_key())
.map(|s| s.to_owned());
if let Some(mut session) = session {
session.tfa = true;
SESSIONS
.lock()
.unwrap()
.insert(self.lr.my_id.clone(), session);
session.conn_id = self.inner.id();
session.conn_type = self.conn_type();
raii::AuthedConnID::insert_session(self.session_key(), session);
} else {
SESSIONS.lock().unwrap().insert(
self.lr.my_id.clone(),
raii::AuthedConnID::insert_session(
self.session_key(),
Session {
name: self.lr.my_name.clone(),
session_id: self.lr.session_id,
last_recv_time: self.last_recv_time.clone(),
random_password: "".to_owned(),
tfa: true,
conn_type: self.conn_type(),
conn_id: self.inner.id(),
},
);
}
@@ -2159,12 +2164,8 @@ impl Connection {
_ => {}
}
if let Some(job_id) = job_id {
self.send(fs::new_error(
job_id,
"one-way-file-transfer-tip",
0,
))
.await;
self.send(fs::new_error(job_id, "one-way-file-transfer-tip", 0))
.await;
return true;
}
}
@@ -2399,7 +2400,10 @@ impl Connection {
}
Some(misc::Union::CloseReason(_)) => {
self.on_close("Peer close", true).await;
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
raii::AuthedConnID::remove_session_if_last_duplication(
self.inner.id(),
self.session_key(),
);
return false;
}
@@ -3159,7 +3163,7 @@ impl Connection {
let mut msg_out = Message::new();
msg_out.set_misc(misc);
self.send(msg_out).await;
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
raii::AuthedConnID::remove_session_if_last_duplication(self.inner.id(), self.session_key());
}
fn read_dir(&mut self, dir: &str, include_hidden: bool) {
@@ -3313,6 +3317,26 @@ impl Connection {
}
}
}
#[inline]
fn conn_type(&self) -> AuthConnType {
if self.file_transfer.is_some() {
AuthConnType::FileTransfer
} else if self.port_forward_socket.is_some() {
AuthConnType::PortForward
} else {
AuthConnType::Remote
}
}
#[inline]
fn session_key(&self) -> SessionKey {
SessionKey {
peer_id: self.lr.my_id.clone(),
name: self.lr.my_name.clone(),
session_id: self.lr.session_id,
}
}
}
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
@@ -3810,15 +3834,18 @@ mod raii {
pub struct AuthedConnID(i32, AuthConnType);
impl AuthedConnID {
pub fn new(id: i32, conn_type: AuthConnType) -> Self {
AUTHED_CONNS.lock().unwrap().push((id, conn_type));
pub fn new(conn_id: i32, conn_type: AuthConnType, session_key: SessionKey) -> Self {
AUTHED_CONNS
.lock()
.unwrap()
.push((conn_id, conn_type, session_key));
Self::check_wake_lock();
use std::sync::Once;
static _ONCE: Once = Once::new();
_ONCE.call_once(|| {
shutdown_hooks::add_shutdown_hook(connection_shutdown_hook);
});
Self(id, conn_type)
Self(conn_id, conn_type)
}
fn check_wake_lock() {
@@ -3843,6 +3870,53 @@ mod raii {
.filter(|c| c.1 == AuthConnType::Remote || c.1 == AuthConnType::FileTransfer)
.count()
}
pub fn remove_session_if_last_duplication(conn_id: i32, key: SessionKey) {
let contains = SESSIONS.lock().unwrap().contains_key(&key);
if contains {
let another = AUTHED_CONNS
.lock()
.unwrap()
.iter()
.any(|c| c.0 != conn_id && c.2 == key && c.1 != AuthConnType::PortForward);
if !another {
// Keep the session if there is another connection with same peer_id and session_id.
SESSIONS.lock().unwrap().remove(&key);
log::info!("remove session");
} else {
log::info!("skip remove session");
}
}
}
pub fn insert_session(key: SessionKey, session: Session) {
let mut insert = true;
if session.conn_type == AuthConnType::PortForward {
// port forward doesn't update last received time
let other_alive_conns = AUTHED_CONNS
.lock()
.unwrap()
.iter()
.filter(|c| {
c.2 == key && c.1 != AuthConnType::PortForward // port forward doesn't remove itself
})
.map(|c| c.0)
.collect::<Vec<_>>();
let another = SESSIONS.lock().unwrap().get(&key).map(|s| {
other_alive_conns.contains(&s.conn_id)
&& s.tfa == session.tfa
&& s.conn_type != AuthConnType::PortForward
}) == Some(true);
if another {
insert = false;
log::info!("skip insert session for port forward");
}
}
if insert {
log::info!("insert session for {:?}", session.conn_type);
SESSIONS.lock().unwrap().insert(key, session);
}
}
}
impl Drop for AuthedConnID {
@@ -3850,7 +3924,7 @@ mod raii {
if self.1 == AuthConnType::Remote {
scrap::codec::Encoder::update(scrap::codec::EncodingUpdate::Remove(self.0));
}
AUTHED_CONNS.lock().unwrap().retain(|&c| c.0 != self.0);
AUTHED_CONNS.lock().unwrap().retain(|c| c.0 != self.0);
let remote_count = AUTHED_CONNS
.lock()
.unwrap()

View File

@@ -506,7 +506,7 @@ impl sciter::EventHandler for SciterSession {
impl SciterSession {
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
let force_relay = args.contains(&"--relay".to_string());
let mut session: Session<SciterHandler> = Session {
let session: Session<SciterHandler> = Session {
password: password.clone(),
args,
server_keyboard_enabled: Arc::new(RwLock::new(true)),
@@ -529,7 +529,7 @@ impl SciterSession {
.lc
.write()
.unwrap()
.initialize(id, conn_type, None, force_relay, None, None);
.initialize(id, conn_type, None, force_relay, None, None, None);
Self(session)
}

View File

@@ -1490,6 +1490,10 @@ impl<T: InvokeUiSession> Session<T> {
msg.set_misc(misc);
self.send(Data::Message(msg));
}
pub fn get_conn_token(&self) -> Option<String> {
self.lc.read().unwrap().get_conn_token()
}
}
pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {