use super::{chancomms::ControlChanMsg, tls::FtpsConfig};
use crate::auth::UserDetail;
use crate::server::chancomms::DataChanCmd;
use crate::server::failed_logins::FailedLoginsCache;
use crate::{
metrics,
storage::{Metadata, StorageBackend},
};
use std::{
fmt::{Debug, Formatter},
net::SocketAddr,
path::PathBuf,
sync::Arc,
};
use tokio::sync::mpsc::{Receiver, Sender};
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct TraceId(u64);
impl TraceId {
pub fn new() -> Self {
let mut id = [0; 8];
getrandom::getrandom(&mut id).expect("Error generating random TraceId");
TraceId(u64::from_ne_bytes(id))
}
}
impl std::fmt::Display for TraceId {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl std::fmt::Debug for TraceId {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum SessionState {
New,
WaitPass,
WaitCmd,
}
pub type SharedSession<S, U> = Arc<tokio::sync::Mutex<Session<S, U>>>;
#[derive(Debug)]
pub struct Session<Storage, User>
where
Storage: StorageBackend<User>,
Storage::Metadata: Metadata,
User: UserDetail,
{
pub trace_id: TraceId,
pub user: Arc<Option<User>>,
pub username: Option<String>,
pub storage: Arc<Storage>,
pub data_cmd_tx: Option<Sender<DataChanCmd>>,
pub data_cmd_rx: Option<Receiver<DataChanCmd>>,
pub data_abort_tx: Option<Sender<()>>,
pub data_abort_rx: Option<Receiver<()>>,
pub control_msg_tx: Option<Sender<ControlChanMsg>>,
pub source: SocketAddr,
pub destination: Option<SocketAddr>,
pub cwd: std::path::PathBuf,
pub rename_from: Option<PathBuf>,
pub state: SessionState,
pub ftps_config: FtpsConfig,
pub cmd_tls: bool,
pub data_tls: bool,
pub collect_metrics: bool,
pub start_pos: u64,
pub data_busy: bool,
pub cert_chain: Option<Vec<crate::auth::ClientCert>>,
pub failed_logins: Option<Arc<FailedLoginsCache>>,
}
impl<Storage, User> Session<Storage, User>
where
Storage: StorageBackend<User> + 'static,
Storage::Metadata: Metadata,
User: UserDetail + 'static,
{
pub(super) fn new(storage: Arc<Storage>, source: SocketAddr) -> Self {
Session {
trace_id: TraceId::new(),
user: Arc::new(None),
username: None,
storage,
data_cmd_tx: None,
data_cmd_rx: None,
data_abort_tx: None,
data_abort_rx: None,
control_msg_tx: None,
source,
destination: None,
cwd: "/".into(),
rename_from: None,
state: SessionState::New,
ftps_config: FtpsConfig::Off,
cmd_tls: false,
data_tls: false,
collect_metrics: false,
start_pos: 0,
data_busy: false,
cert_chain: None,
failed_logins: None,
}
}
pub fn ftps(mut self, mode: FtpsConfig) -> Self {
self.ftps_config = mode;
self
}
pub fn metrics(mut self, collect_metrics: bool) -> Self {
if collect_metrics {
metrics::inc_session();
}
self.collect_metrics = collect_metrics;
self
}
pub fn control_msg_tx(mut self, sender: Sender<ControlChanMsg>) -> Self {
self.control_msg_tx = Some(sender);
self
}
pub fn destination(mut self, destination: Option<SocketAddr>) -> Self {
self.destination = destination;
self
}
pub fn failed_logins(mut self, failed_logins: Option<Arc<FailedLoginsCache>>) -> Self {
self.failed_logins = failed_logins;
self
}
}
impl<Storage, User> Drop for Session<Storage, User>
where
Storage: StorageBackend<User>,
Storage::Metadata: Metadata,
User: UserDetail,
{
fn drop(&mut self) {
if self.collect_metrics {
metrics::dec_session();
}
}
}