diff --git a/src/main.rs b/src/main.rs index fb5ada6..bf34fd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use std::{net::SocketAddr, sync::Arc, time::Duration}; +use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration}; use russh::{keys::ssh_key::{rand_core::OsRng, PublicKey}, server::{Auth, Handler as SshHandler, Msg, Server, Session}, Channel, ChannelId, Error}; -use russh_sftp::{protocol::{Name, StatusCode}, server::Handler as SftpHandler}; +use russh_sftp::{protocol::{File, FileAttributes, Handle, Name, Status, StatusCode}, server::Handler as SftpHandler}; struct SftpServer; @@ -10,59 +10,65 @@ impl Server for SftpServer { type Handler = SshSession; fn new_client(&mut self, _peer_addr: Option) -> Self::Handler { - SshSession{ channel: None } + SshSession{ channel: None, user: None } } } struct SshSession { - channel: Option> + channel: Option>, + user: Option } impl SshHandler for SshSession { type Error = Error; async fn auth_publickey_offered( - &mut self, - user: &str, - public_key: &PublicKey, - ) -> Result { + &mut self, + user: &str, + public_key: &PublicKey, + ) -> Result { + let _ = public_key; + self.user = Some(user.to_string()); Ok(Auth::Accept) } async fn auth_publickey( - &mut self, - user: &str, - public_key: &PublicKey, - ) -> Result { + &mut self, + user: &str, + public_key: &PublicKey, + ) -> Result { + let _ = user; + let _ = public_key; Ok(Auth::Accept) } async fn channel_open_session( - &mut self, - channel: russh::Channel, - _session: &mut Session, - ) -> Result { + &mut self, + channel: russh::Channel, + _session: &mut Session, + ) -> Result { self.channel = Some(channel); Ok(true) } async fn channel_eof( - &mut self, - channel_id: ChannelId, - session: &mut Session, - ) -> Result<(), Self::Error> { + &mut self, + channel_id: ChannelId, + session: &mut Session, + ) -> Result<(), Self::Error> { session.close(channel_id) } async fn subsystem_request( - &mut self, - channel_id: ChannelId, - name: &str, - session: &mut Session, - ) -> Result<(), Self::Error> { + &mut self, + channel_id: ChannelId, + name: &str, + session: &mut Session, + ) -> Result<(), Self::Error> { if name == "sftp" { session.channel_success(channel_id)?; - let sftp_handler = SftpSession {}; + let root_dir = format!("/srv/sftp/{}", self.user.take().unwrap()); + let sftp_handler = SftpSession { cwd: root_dir.clone(), root_dir, handle_map: HashMap::new() }; russh_sftp::server::run(self.channel.take().ok_or(Self::Error::WrongChannel)?.into_stream(), sftp_handler).await; } else { @@ -72,7 +78,11 @@ impl SshHandler for SshSession { } } -struct SftpSession; +struct SftpSession { + root_dir: String, + cwd: String, + handle_map: HashMap +} impl SftpHandler for SftpSession { type Error = StatusCode; @@ -81,6 +91,64 @@ impl SftpHandler for SftpSession { Self::Error::OpUnsupported } + async fn realpath( + &mut self, + id: u32, + path: String, + ) -> Result { + let paths = path.split('/'); + for path_part in paths { + match path_part { + ".." => { + if self.cwd != self.root_dir { + if let Some(pos) = self.cwd.rfind('/') { + self.cwd.truncate(pos); + } + } + }, + "." => {}, + _ => self.cwd.push_str(&format!("/{}", path_part)) + } + } + + Ok(Name { id, files: vec![File::dummy(&self.cwd)] }) + } + + async fn opendir( + &mut self, + id: u32, + path: String, + ) -> Result { + self.handle_map.insert(path.clone(), false); + Ok(Handle { id, handle: path }) + } + + async fn readdir( + &mut self, + id: u32, + handle: String, + ) -> Result { + if !self.handle_map.get(&handle).unwrap() { + *self.handle_map.get_mut(&handle).unwrap() = true; + return Ok(Name { id, files: vec![File::new("test", FileAttributes::default())] }) + } + Err(StatusCode::Eof) + } + + async fn close( + &mut self, + id: u32, + handle: String, + ) -> Result { + self.handle_map.remove(&handle); + Ok(Status { + id, + status_code: StatusCode::Ok, + error_message: "Ok".to_string(), + language_tag: "en-US".to_string(), + }) + } + }