mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
fix: avoid sync I/O in async functions
This commit is contained in:
parent
3909f55097
commit
f5c13ebca3
1 changed files with 46 additions and 56 deletions
|
|
@ -1,9 +1,8 @@
|
||||||
use std::{io, sync::Arc, time::Duration};
|
use std::{io, sync::Arc, time::{Duration, SystemTime}};
|
||||||
|
|
||||||
use russh::keys::PrivateKeyWithHashAlg;
|
use russh::keys::PrivateKeyWithHashAlg;
|
||||||
use yazi_config::vfs::ServiceSftp;
|
use yazi_config::vfs::ServiceSftp;
|
||||||
use yazi_fs::provider::local::Local;
|
use yazi_fs::provider::local::Local;
|
||||||
use yazi_shared::timestamp_us;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub(super) struct Conn {
|
pub(super) struct Conn {
|
||||||
|
|
@ -11,6 +10,12 @@ pub(super) struct Conn {
|
||||||
pub(super) config: &'static ServiceSftp,
|
pub(super) config: &'static ServiceSftp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! cfg_err {
|
||||||
|
($($args:tt)*) => {
|
||||||
|
russh::Error::InvalidConfig(format!($($args)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl russh::client::Handler for Conn {
|
impl russh::client::Handler for Conn {
|
||||||
type Error = russh::Error;
|
type Error = russh::Error;
|
||||||
|
|
||||||
|
|
@ -102,7 +107,7 @@ impl Conn {
|
||||||
pref: Arc<russh::client::Config>,
|
pref: Arc<russh::client::Config>,
|
||||||
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
||||||
let Some(password) = &self.config.password else {
|
let Some(password) = &self.config.password else {
|
||||||
return Err(russh::Error::InvalidConfig("Password not provided".to_owned()));
|
return Err(cfg_err!("Password not provided"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut session =
|
let mut session =
|
||||||
|
|
@ -111,7 +116,7 @@ impl Conn {
|
||||||
if session.authenticate_password(&self.config.user, password).await?.success() {
|
if session.authenticate_password(&self.config.user, password).await?.success() {
|
||||||
Ok(session)
|
Ok(session)
|
||||||
} else {
|
} else {
|
||||||
Err(russh::Error::InvalidConfig("Password authentication failed".to_owned()))
|
Err(cfg_err!("Password authentication failed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,14 +126,13 @@ impl Conn {
|
||||||
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
||||||
let key_file = &self.config.key_file;
|
let key_file = &self.config.key_file;
|
||||||
if key_file.as_os_str().is_empty() {
|
if key_file.as_os_str().is_empty() {
|
||||||
return Err(russh::Error::InvalidConfig("Key file not provided".to_owned()));
|
return Err(cfg_err!("Key file not provided"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let key = Local::regular(key_file)
|
let key = Local::regular(key_file)
|
||||||
.read_to_string()
|
.read_to_string()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| russh::Error::InvalidConfig(format!("Failed to read key file: {e}")))?;
|
.map_err(|e| cfg_err!("Failed to read key file: {e}"))?;
|
||||||
|
|
||||||
let key = russh::keys::decode_secret_key(&key, self.config.key_passphrase.as_deref())?;
|
let key = russh::keys::decode_secret_key(&key, self.config.key_passphrase.as_deref())?;
|
||||||
|
|
||||||
let mut session =
|
let mut session =
|
||||||
|
|
@ -144,11 +148,7 @@ impl Conn {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if result.success() {
|
if result.success() { Ok(session) } else { Err(cfg_err!("Public key authentication failed")) }
|
||||||
Ok(session)
|
|
||||||
} else {
|
|
||||||
Err(russh::Error::InvalidConfig("Public key authentication failed".to_owned()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connect_by_key_and_cert(
|
async fn connect_by_key_and_cert(
|
||||||
|
|
@ -157,62 +157,52 @@ impl Conn {
|
||||||
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
||||||
let key_file = &self.config.key_file;
|
let key_file = &self.config.key_file;
|
||||||
if key_file.as_os_str().is_empty() {
|
if key_file.as_os_str().is_empty() {
|
||||||
return Err(russh::Error::InvalidConfig("Key file not provided".to_owned()));
|
return Err(cfg_err!("Key file not provided"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let cert_file = &self.config.cert_file;
|
let cert_file = &self.config.cert_file;
|
||||||
if cert_file.as_os_str().is_empty() {
|
if cert_file.as_os_str().is_empty() {
|
||||||
return Err(russh::Error::InvalidConfig("Cert file not provided".to_owned()));
|
return Err(cfg_err!("Cert file not provided"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let cert = russh::keys::load_openssh_certificate(cert_file).map_err(
|
// Decode the key and cert files
|
||||||
|e: russh::keys::ssh_key::Error| {
|
|
||||||
russh::Error::InvalidConfig(format!("Failed to read cert file: {e}"))
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if !self.config.no_cert_verify {
|
|
||||||
cert.verify_signature().map_err(|e| {
|
|
||||||
russh::Error::InvalidConfig(format!("Certificate signature verification failed: {e}"))
|
|
||||||
})?;
|
|
||||||
let unix_timestamp = timestamp_us() / 1_000_000;
|
|
||||||
|
|
||||||
if unix_timestamp < cert.valid_after() || unix_timestamp > cert.valid_before() {
|
|
||||||
let format_time = |ts: u64| {
|
|
||||||
i64::try_from(ts)
|
|
||||||
.ok()
|
|
||||||
.and_then(chrono::DateTime::from_timestamp_secs)
|
|
||||||
.map(|dt| dt.with_timezone(&chrono::Local).format("%Y-%m-%d %H:%M:%S").to_string())
|
|
||||||
.unwrap_or_else(|| "unknown date".to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
let start = format_time(cert.valid_after());
|
|
||||||
let end = format_time(cert.valid_before());
|
|
||||||
|
|
||||||
return Err(russh::Error::InvalidConfig(format!(
|
|
||||||
"Certificate is invalid. The validity range is {} to {}",
|
|
||||||
start, end
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let key = Local::regular(key_file)
|
let key = Local::regular(key_file)
|
||||||
.read_to_string()
|
.read_to_string()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| russh::Error::InvalidConfig(format!("Failed to read key file: {e}")))?;
|
.map_err(|e| cfg_err!("Failed to read key file: {e}"))?;
|
||||||
|
|
||||||
let key = russh::keys::decode_secret_key(&key, self.config.key_passphrase.as_deref())?;
|
let key = russh::keys::decode_secret_key(&key, self.config.key_passphrase.as_deref())?;
|
||||||
|
|
||||||
|
let cert = Local::regular(cert_file)
|
||||||
|
.read_to_string()
|
||||||
|
.await
|
||||||
|
.map_err(|e| cfg_err!("Failed to read cert file: {e}"))?;
|
||||||
|
let cert = russh::keys::Certificate::from_openssh(&cert)?;
|
||||||
|
|
||||||
|
// Verify the certificate
|
||||||
|
if !self.config.no_cert_verify {
|
||||||
|
cert
|
||||||
|
.verify_signature()
|
||||||
|
.map_err(|e| cfg_err!("Certificate signature verification failed: {e}"))?;
|
||||||
|
|
||||||
|
let now: chrono::DateTime<chrono::Local> = SystemTime::now().into();
|
||||||
|
let start: chrono::DateTime<chrono::Local> = cert.valid_after_time().into();
|
||||||
|
let end: chrono::DateTime<chrono::Local> = cert.valid_before_time().into();
|
||||||
|
if now < start || now > end {
|
||||||
|
return Err(cfg_err!(
|
||||||
|
"Certificate is out of the validity range of '{}' to '{}'",
|
||||||
|
start.to_rfc2822(),
|
||||||
|
end.to_rfc2822()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut session =
|
let mut session =
|
||||||
russh::client::connect(pref, (self.config.host.as_str(), self.config.port), self).await?;
|
russh::client::connect(pref, (self.config.host.as_str(), self.config.port), self).await?;
|
||||||
|
|
||||||
let result = session.authenticate_openssh_cert(&self.config.user, Arc::new(key), cert).await?;
|
if session.authenticate_openssh_cert(&self.config.user, Arc::new(key), cert).await?.success() {
|
||||||
|
|
||||||
if result.success() {
|
|
||||||
Ok(session)
|
Ok(session)
|
||||||
} else {
|
} else {
|
||||||
Err(russh::Error::InvalidConfig(
|
Err(cfg_err!("Public key with certificate authentication failed"))
|
||||||
"Public key with certificate authentication failed".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,7 +212,7 @@ impl Conn {
|
||||||
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
||||||
let identity_agent = &self.config.identity_agent;
|
let identity_agent = &self.config.identity_agent;
|
||||||
if identity_agent.as_os_str().is_empty() {
|
if identity_agent.as_os_str().is_empty() {
|
||||||
return Err(russh::Error::InvalidConfig("Identity agent not provided".to_owned()));
|
return Err(cfg_err!("Identity agent not provided"));
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
@ -233,7 +223,7 @@ impl Conn {
|
||||||
|
|
||||||
let keys = agent.request_identities().await?;
|
let keys = agent.request_identities().await?;
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return Err(russh::Error::InvalidConfig("No keys found in SSH agent".to_owned()));
|
return Err(cfg_err!("No keys found in SSH agent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut session =
|
let mut session =
|
||||||
|
|
@ -249,6 +239,6 @@ impl Conn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(russh::Error::InvalidConfig("Public key authentication via agent failed".to_owned()))
|
Err(cfg_err!("Public key authentication via agent failed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue