From 899d5a1e17d8f988b2a88c8c286104407fb95de3 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Fri, 4 Jul 2025 12:02:10 +0500 Subject: [PATCH 01/10] added some fns --- src/main.rs | 122 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 95 insertions(+), 27 deletions(-) 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(), + }) + } + } From 611f29f32567171205bd2a9e398ddac107f04fb4 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sat, 5 Jul 2025 20:09:01 +0500 Subject: [PATCH 02/10] new file and progress --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 88 ++++------------------------------------ src/sftp.rs | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 81 deletions(-) create mode 100644 src/sftp.rs diff --git a/Cargo.lock b/Cargo.lock index 7a19278..c711862 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,6 +534,7 @@ dependencies = [ name = "flux-sftp" version = "0.1.0" dependencies = [ + "chrono", "russh", "russh-sftp", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 138143d..ab36e39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +chrono = "0.4.41" russh = "0.52.1" russh-sftp = "2.1.1" tokio = { version = "1.45.1", features = ["full"] } diff --git a/src/main.rs b/src/main.rs index bf34fd7..68074ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -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::{File, FileAttributes, Handle, Name, Status, StatusCode}, server::Handler as SftpHandler}; +mod sftp; +use std::{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}; +use sftp::SftpSession; struct SftpServer; @@ -20,7 +20,7 @@ struct SshSession { } impl SshHandler for SshSession { - type Error = Error; + type Error = russh::Error; async fn auth_publickey_offered( &mut self, @@ -67,8 +67,8 @@ impl SshHandler for SshSession { ) -> Result<(), Self::Error> { if name == "sftp" { session.channel_success(channel_id)?; - let root_dir = format!("/srv/sftp/{}", self.user.take().unwrap()); - let sftp_handler = SftpSession { cwd: root_dir.clone(), root_dir, handle_map: HashMap::new() }; + let jail_dir = format!("/srv/sftp/{}", self.user.take().unwrap()); + let sftp_handler = SftpSession::new(jail_dir); russh_sftp::server::run(self.channel.take().ok_or(Self::Error::WrongChannel)?.into_stream(), sftp_handler).await; } else { @@ -78,80 +78,6 @@ impl SshHandler for SshSession { } } -struct SftpSession { - root_dir: String, - cwd: String, - handle_map: HashMap -} - -impl SftpHandler for SftpSession { - type Error = StatusCode; - - fn unimplemented(&self) -> Self::Error { - 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(), - }) - } - -} - - #[tokio::main] async fn main() { diff --git a/src/sftp.rs b/src/sftp.rs new file mode 100644 index 0000000..10a6327 --- /dev/null +++ b/src/sftp.rs @@ -0,0 +1,114 @@ +use std::{collections::HashMap, os::unix::fs::MetadataExt}; + +use chrono::{Local, TimeZone}; +use russh_sftp::{protocol::{File, FileAttributes, Handle, Name, Status, StatusCode}, server::Handler as SftpHandler}; + +use tokio::fs::{self, ReadDir}; + +pub struct SftpSession { + jail_dir: String, + cwd: String, + handles: HashMap +} + +impl SftpSession { + pub fn new(jail_dir: String) -> Self { + SftpSession { jail_dir, cwd: String::from("/"), handles: HashMap::new() } + } +} + +impl SftpHandler for SftpSession { + type Error = StatusCode; + + fn unimplemented(&self) -> Self::Error { + StatusCode::OpUnsupported + } + + async fn realpath( + &mut self, + id: u32, + path: String, + ) -> Result { + let path_parts = path.split('/'); + for path_part in path_parts { + match path_part { + ".." => { + if self.cwd != "/" { + 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 { + let path = format!("{}/{}", self.jail_dir, path); + match fs::read_dir(&path).await { + Ok(entries) => { + self.handles.insert(path.clone(), entries); + Ok(Handle { id, handle: path }) + } + Err(e) => { + println!("Error in reading dir: {}", e); + Err(StatusCode::NoSuchFile) + } + } + } + + async fn readdir( + &mut self, + id: u32, + handle: String, + ) -> Result { + match self.handles.get_mut(&handle).unwrap().next_entry().await { + Ok(Some(entry)) => { + let metadata = entry.metadata().await.unwrap(); + let dt = Local.timestamp_opt(metadata.mtime(), 0).unwrap(); + let longname = format!("{} {} {}", metadata.size(), dt.format("%b %e %Y"), entry.file_name().to_string_lossy()); + Ok(Name { id, files: vec![ + File { + filename: entry.file_name().to_string_lossy().into(), + longname: longname, + attrs: FileAttributes { + size: Some(metadata.size()), + atime: Some(metadata.atime() as u32), + mtime: Some(metadata.mtime() as u32), + ..Default::default() + } + } + ] }) + } + Ok(None) => Err(StatusCode::Eof), + Err(e) => { + println!("Error listing file: {}", e); + Err(StatusCode::Failure) + } + } + } + + async fn close( + &mut self, + id: u32, + handle: String, + ) -> Result { + self.handles.remove(&handle); + Ok(Status { + id, + status_code: StatusCode::Ok, + error_message: "Ok".to_string(), + language_tag: "en-US".to_string(), + }) + } + +} + From 34db55a70c67ad8e30be5a59feee34f938d936bf Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 06:33:59 +0500 Subject: [PATCH 03/10] stats opening and reading file, very poggers --- Cargo.lock | 39 +++++++++ Cargo.toml | 1 + src/main.rs | 2 +- src/sftp.rs | 224 ++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 224 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c711862..9301c3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -535,6 +544,7 @@ name = "flux-sftp" version = "0.1.0" dependencies = [ "chrono", + "regex", "russh", "russh-sftp", "tokio", @@ -1222,6 +1232,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rfc6979" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index ab36e39..4f47fdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] chrono = "0.4.41" +regex = "1.11.1" russh = "0.52.1" russh-sftp = "2.1.1" tokio = { version = "1.45.1", features = ["full"] } diff --git a/src/main.rs b/src/main.rs index 68074ca..7f68005 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,7 +67,7 @@ impl SshHandler for SshSession { ) -> Result<(), Self::Error> { if name == "sftp" { session.channel_success(channel_id)?; - let jail_dir = format!("/srv/sftp/{}", self.user.take().unwrap()); + let jail_dir = format!("/srv/sftp/{}", self.user.as_ref().unwrap()); let sftp_handler = SftpSession::new(jail_dir); russh_sftp::server::run(self.channel.take().ok_or(Self::Error::WrongChannel)?.into_stream(), sftp_handler).await; } diff --git a/src/sftp.rs b/src/sftp.rs index 10a6327..da95d72 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -1,14 +1,20 @@ -use std::{collections::HashMap, os::unix::fs::MetadataExt}; +use std::{collections::HashMap, io::SeekFrom, os::unix::fs::MetadataExt}; use chrono::{Local, TimeZone}; -use russh_sftp::{protocol::{File, FileAttributes, Handle, Name, Status, StatusCode}, server::Handler as SftpHandler}; +use regex::Regex; +use russh_sftp::{protocol::{Attrs, Data, File, FileAttributes, Handle as SftpHandle, Name, OpenFlags, Status, StatusCode}, server::Handler as SftpHandler}; -use tokio::fs::{self, ReadDir}; +use tokio::{fs::{self, OpenOptions, ReadDir}, io::{AsyncReadExt, AsyncSeekExt}}; + +enum Handle { + Dir(ReadDir), + File(fs::File) +} pub struct SftpSession { jail_dir: String, cwd: String, - handles: HashMap + handles: HashMap } impl SftpSession { @@ -29,34 +35,107 @@ impl SftpHandler for SftpSession { id: u32, path: String, ) -> Result { - let path_parts = path.split('/'); - for path_part in path_parts { - match path_part { - ".." => { - if self.cwd != "/" { - if let Some(pos) = self.cwd.rfind('/') { - self.cwd.truncate(pos); - } - } - }, - "." => {}, - _ => self.cwd.push_str(&format!("/{}", path_part)) - } + println!("realpath called, path: {}", path); + + let re_1 = Regex::new(r"/[^/]+/\.\.").unwrap(); + self.cwd = re_1.replace_all(&path, "").to_string(); + while re_1.is_match(&self.cwd) { + self.cwd = re_1.replace_all(&self.cwd, "").to_string(); + } + + let re_2 = Regex::new(r"/\.").unwrap(); + self.cwd = re_2.replace_all(&self.cwd, "").to_string(); + + if self.cwd == "." || self.cwd == "" { + self.cwd = String::from("/"); } Ok(Name { id, files: vec![File::dummy(&self.cwd)] }) } + async fn open( + &mut self, + id: u32, + filename: String, + pflags: OpenFlags, + _attrs: FileAttributes, + ) -> Result { + println!("open called, path: {}", filename); + let filename = format!("{}/{}", self.jail_dir, filename); + let mut options = OpenOptions::new(); + if pflags.contains(OpenFlags::READ){ + options.read(true); + } + if pflags.contains(OpenFlags::WRITE){ + options.write(true); + } + if pflags.contains(OpenFlags::APPEND){ + options.append(true); + } + if pflags.contains(OpenFlags::CREATE){ + options.create(true); + } + if pflags.contains(OpenFlags::TRUNCATE){ + options.truncate(true); + } + match options.open(&filename).await { + Ok(file) => { + self.handles.insert(filename.clone(), Handle::File(file)); + Ok(SftpHandle { id, handle: filename }) + } + Err(_) => Err(StatusCode::NoSuchFile) + } + } + + async fn read( + &mut self, + id: u32, + handle: String, + offset: u64, + len: u32, + ) -> Result { + if let Handle::File(file) = self.handles.get_mut(&handle).unwrap() { + let mut buf = vec![0u8; len as usize]; + match file.seek(SeekFrom::Start(offset)).await { + Ok(_) => { + match file.read(&mut buf).await { + Ok(bytes) => { + if bytes != 0 { + buf.truncate(bytes); + Ok(Data { id, data: buf }) + } + else { + Err(StatusCode::Eof) + } + } + Err(e) => { + println!("Error in reading from offset in file: {}", e); + Err(StatusCode::Failure) + } + } + } + Err(e) => { + println!("Error in seeking offset in file: {}", e); + Err(StatusCode::Failure) + } + } + } + else { + Err(StatusCode::Ok) + } + } + async fn opendir( &mut self, id: u32, path: String, - ) -> Result { + ) -> Result { + println!("opendir called"); let path = format!("{}/{}", self.jail_dir, path); match fs::read_dir(&path).await { Ok(entries) => { - self.handles.insert(path.clone(), entries); - Ok(Handle { id, handle: path }) + self.handles.insert(path.clone(), Handle::Dir(entries)); + Ok(SftpHandle { id, handle: path }) } Err(e) => { println!("Error in reading dir: {}", e); @@ -70,30 +149,37 @@ impl SftpHandler for SftpSession { id: u32, handle: String, ) -> Result { - match self.handles.get_mut(&handle).unwrap().next_entry().await { - Ok(Some(entry)) => { - let metadata = entry.metadata().await.unwrap(); - let dt = Local.timestamp_opt(metadata.mtime(), 0).unwrap(); - let longname = format!("{} {} {}", metadata.size(), dt.format("%b %e %Y"), entry.file_name().to_string_lossy()); - Ok(Name { id, files: vec![ - File { - filename: entry.file_name().to_string_lossy().into(), - longname: longname, - attrs: FileAttributes { - size: Some(metadata.size()), - atime: Some(metadata.atime() as u32), - mtime: Some(metadata.mtime() as u32), - ..Default::default() + println!("readdir called"); + if let Handle::Dir(handle) = self.handles.get_mut(&handle).unwrap() { + match handle.next_entry().await { + Ok(Some(entry)) => { + let metadata = entry.metadata().await.unwrap(); + let dt = Local.timestamp_opt(metadata.mtime(), 0).unwrap(); + let longname = format!("{} {} {}", metadata.size(), dt.format("%b %e %Y"), entry.file_name().to_string_lossy()); + Ok(Name { id, files: vec![ + File { + filename: entry.file_name().to_string_lossy().into(), + longname: longname, + attrs: FileAttributes { + size: Some(metadata.size()), + atime: Some(metadata.atime() as u32), + mtime: Some(metadata.mtime() as u32), + ..Default::default() + } } - } - ] }) - } - Ok(None) => Err(StatusCode::Eof), - Err(e) => { - println!("Error listing file: {}", e); - Err(StatusCode::Failure) + ] }) + } + Ok(None) => Err(StatusCode::Eof), + Err(e) => { + println!("Error listing file: {}", e); + Err(StatusCode::Failure) + } } } + else { + println!("handle is not a dirhandle"); + Err(StatusCode::Failure) + } } async fn close( @@ -101,6 +187,7 @@ impl SftpHandler for SftpSession { id: u32, handle: String, ) -> Result { + println!("close called"); self.handles.remove(&handle); Ok(Status { id, @@ -110,5 +197,60 @@ impl SftpHandler for SftpSession { }) } + async fn stat( + &mut self, + id: u32, + path: String, + ) -> Result { + println!("stat called"); + let path = format!("{}/{}", self.jail_dir, path); + match fs::metadata(path).await { + Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { + size: Some(metadata.size()), + uid: Some(metadata.uid()), + user: None, + gid: Some(metadata.gid()), + group: None, + permissions: Some(metadata.mode()), + atime: Some(metadata.atime() as u32), + mtime: Some(metadata.mtime() as u32), + ..Default::default() + }}), + Err(_) => Err(StatusCode::NoSuchFile) + } + } + + async fn lstat( + &mut self, + id: u32, + path: String, + ) -> Result { + println!("lstat called"); + let path = format!("{}/{}", self.jail_dir, path); + match fs::symlink_metadata(path).await { + Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { + size: Some(metadata.size()), + uid: Some(metadata.uid()), + user: None, + gid: Some(metadata.gid()), + group: None, + permissions: Some(metadata.mode()), + atime: Some(metadata.atime() as u32), + mtime: Some(metadata.mtime() as u32) + }}), + Err(_) => Err(StatusCode::OpUnsupported) + } + + } + + async fn fstat( + &mut self, + id: u32, + handle: String, + ) -> Result { + println!("fstat called"); + self.stat(id, handle).await + } + } From 464eb19ee4780547195ba69480371b1f87d33776 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 07:05:12 +0500 Subject: [PATCH 04/10] write added so much pogggers --- src/sftp.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/sftp.rs b/src/sftp.rs index da95d72..335913e 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -1,10 +1,10 @@ -use std::{collections::HashMap, io::SeekFrom, os::unix::fs::MetadataExt}; +use std::{collections::HashMap, io::{ErrorKind, SeekFrom}, os::unix::fs::MetadataExt}; use chrono::{Local, TimeZone}; use regex::Regex; use russh_sftp::{protocol::{Attrs, Data, File, FileAttributes, Handle as SftpHandle, Name, OpenFlags, Status, StatusCode}, server::Handler as SftpHandler}; -use tokio::{fs::{self, OpenOptions, ReadDir}, io::{AsyncReadExt, AsyncSeekExt}}; +use tokio::{fs::{self, OpenOptions, ReadDir}, io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}}; enum Handle { Dir(ReadDir), @@ -83,7 +83,16 @@ impl SftpHandler for SftpSession { self.handles.insert(filename.clone(), Handle::File(file)); Ok(SftpHandle { id, handle: filename }) } - Err(_) => Err(StatusCode::NoSuchFile) + Err(e) => { + println!("error opeing file: {}", e); + match e.kind() { + ErrorKind::NotFound => Err(StatusCode::NoSuchFile), + ErrorKind::PermissionDenied => Err(StatusCode::PermissionDenied), + ErrorKind::ConnectionReset => Err(StatusCode::ConnectionLost), + ErrorKind::NotConnected => Err(StatusCode::NoConnection), + _ => Err(StatusCode::Failure) + } + } } } @@ -121,7 +130,45 @@ impl SftpHandler for SftpSession { } } else { - Err(StatusCode::Ok) + println!("handle is not a filehandle"); + Err(StatusCode::Failure) + } + } + + async fn write( + &mut self, + id: u32, + handle: String, + offset: u64, + data: Vec, + ) -> Result { + if let Handle::File(file) = self.handles.get_mut(&handle).unwrap() { + match file.seek(SeekFrom::Start(offset)).await { + Ok(_) => { + match file.write(&data).await { + Ok(_) => { + Ok(Status { + id, + status_code: StatusCode::Ok, + error_message: "Ok".to_string(), + language_tag: "en-US".to_string(), + }) + } + Err(e) => { + println!("Error in writing at offset in file: {}", e); + Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + Err(e) => { + println!("Error in seeking offset in file: {}", e); + Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + else { + println!("handle is not a filehandle"); + Err(StatusCode::Failure) } } From 2840b5bed725b069e876517b4ed9cf47f4684959 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 07:17:51 +0500 Subject: [PATCH 05/10] yoo, nautlius also work now --- src/sftp.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sftp.rs b/src/sftp.rs index 335913e..81147d9 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -177,7 +177,7 @@ impl SftpHandler for SftpSession { id: u32, path: String, ) -> Result { - println!("opendir called"); + println!("opendir called: {}", path); let path = format!("{}/{}", self.jail_dir, path); match fs::read_dir(&path).await { Ok(entries) => { @@ -209,9 +209,13 @@ impl SftpHandler for SftpSession { longname: longname, attrs: FileAttributes { size: Some(metadata.size()), + uid: Some(metadata.uid()), + user: None, + gid: Some(metadata.gid()), + group: None, + permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), - ..Default::default() } } ] }) @@ -249,7 +253,7 @@ impl SftpHandler for SftpSession { id: u32, path: String, ) -> Result { - println!("stat called"); + println!("stat called: {}", path); let path = format!("{}/{}", self.jail_dir, path); match fs::metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { @@ -261,7 +265,6 @@ impl SftpHandler for SftpSession { permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), - ..Default::default() }}), Err(_) => Err(StatusCode::NoSuchFile) } @@ -272,7 +275,7 @@ impl SftpHandler for SftpSession { id: u32, path: String, ) -> Result { - println!("lstat called"); + println!("lstat called: {}", path); let path = format!("{}/{}", self.jail_dir, path); match fs::symlink_metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { From 2328982583df4dbcf127c823152b1b080ee74538 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 07:56:51 +0500 Subject: [PATCH 06/10] rm mkdir and rmdir pooog --- src/sftp.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/src/sftp.rs b/src/sftp.rs index 81147d9..87d84f0 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -209,13 +209,14 @@ impl SftpHandler for SftpSession { longname: longname, attrs: FileAttributes { size: Some(metadata.size()), - uid: Some(metadata.uid()), - user: None, - gid: Some(metadata.gid()), - group: None, + // uid: Some(metadata.uid()), + // user: None, + // gid: Some(metadata.gid()), + // group: None, permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), + ..Default::default() } } ] }) @@ -258,13 +259,14 @@ impl SftpHandler for SftpSession { match fs::metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { size: Some(metadata.size()), - uid: Some(metadata.uid()), - user: None, - gid: Some(metadata.gid()), - group: None, + // uid: Some(metadata.uid()), + // user: None, + // gid: Some(metadata.gid()), + // group: None, permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), + ..Default::default() }}), Err(_) => Err(StatusCode::NoSuchFile) } @@ -280,13 +282,14 @@ impl SftpHandler for SftpSession { match fs::symlink_metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { size: Some(metadata.size()), - uid: Some(metadata.uid()), - user: None, - gid: Some(metadata.gid()), - group: None, + // uid: Some(metadata.uid()), + // user: None, + // gid: Some(metadata.gid()), + // group: None, permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), - mtime: Some(metadata.mtime() as u32) + mtime: Some(metadata.mtime() as u32), + ..Default::default() }}), Err(_) => Err(StatusCode::OpUnsupported) } @@ -302,5 +305,72 @@ impl SftpHandler for SftpSession { self.stat(id, handle).await } + async fn remove( + &mut self, + id: u32, + filename: String, + ) -> Result { + println!("remove called: {}", filename); + let filename = format!("{}/{}", self.jail_dir, filename); + match fs::remove_file(filename).await { + Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), + Err(e) => { + println!("error removing file: {}", e); + match e.kind() { + ErrorKind::NotFound => Ok(Status { id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), + _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + } + + async fn mkdir( + &mut self, + id: u32, + path: String, + _attrs: FileAttributes, + ) -> Result { + println!("mkdir called: {}", path); + let path = format!("{}/{}", self.jail_dir, path); + match fs::create_dir(&path).await { + Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), + Err(e) => { + println!("error removing file: {}", e); + match e.kind() { + ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), + _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + + } + + async fn rmdir( + &mut self, + id: u32, + path: String, + ) -> Result { + println!("rmdir called: {}", path); + let path = format!("{}/{}", self.jail_dir, path); + match fs::remove_dir(path).await { + Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), + Err(e) => { + println!("error removing file: {}", e); + match e.kind() { + ErrorKind::NotFound => Ok(Status { id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), + _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + } + } From f1bc7e9a1b72e865133ccddd39999a0004d02c04 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 08:14:21 +0500 Subject: [PATCH 07/10] rename pogg --- src/sftp.rs | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/sftp.rs b/src/sftp.rs index 87d84f0..e34f95f 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -63,21 +63,12 @@ impl SftpHandler for SftpSession { println!("open called, path: {}", filename); let filename = format!("{}/{}", self.jail_dir, filename); let mut options = OpenOptions::new(); - if pflags.contains(OpenFlags::READ){ - options.read(true); - } - if pflags.contains(OpenFlags::WRITE){ - options.write(true); - } - if pflags.contains(OpenFlags::APPEND){ - options.append(true); - } - if pflags.contains(OpenFlags::CREATE){ - options.create(true); - } - if pflags.contains(OpenFlags::TRUNCATE){ - options.truncate(true); - } + options + .read(pflags.contains(OpenFlags::READ)) + .write(pflags.contains(OpenFlags::WRITE)) + .append(pflags.contains(OpenFlags::APPEND)) + .create(pflags.contains(OpenFlags::CREATE)) + .truncate(pflags.contains(OpenFlags::TRUNCATE)); match options.open(&filename).await { Ok(file) => { self.handles.insert(filename.clone(), Handle::File(file)); @@ -143,10 +134,11 @@ impl SftpHandler for SftpSession { data: Vec, ) -> Result { if let Handle::File(file) = self.handles.get_mut(&handle).unwrap() { + match file.seek(SeekFrom::Start(offset)).await { Ok(_) => { - match file.write(&data).await { - Ok(_) => { + match file.write_all(&data).await { + Ok(()) => { Ok(Status { id, status_code: StatusCode::Ok, @@ -372,5 +364,30 @@ impl SftpHandler for SftpSession { } } + async fn rename( + &mut self, + id: u32, + oldpath: String, + newpath: String, + ) -> Result { + println!("rename called from: {}, to: {}", oldpath, newpath); + let oldpath = format!("{}/{}", self.jail_dir, oldpath); + let newpath = format!("{}/{}", self.jail_dir, newpath); + + match fs::rename(oldpath, newpath).await { + Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), + Err(e) => { + println!("error removing file: {}", e); + match e.kind() { + ErrorKind::NotFound => Ok(Status { id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), + _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + } + } From 649263ff5fbf76dd3f5b653d0ff0bb1f9ea01aae Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 14:45:01 +0500 Subject: [PATCH 08/10] update fstat and fix exclude opening files --- src/sftp.rs | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/sftp.rs b/src/sftp.rs index e34f95f..d98eeae 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -61,7 +61,12 @@ impl SftpHandler for SftpSession { _attrs: FileAttributes, ) -> Result { println!("open called, path: {}", filename); - let filename = format!("{}/{}", self.jail_dir, filename); + println!("pflags raw: {:b}", pflags.bits()); + println!("pflags: read: {}, write: {}, append: {}, create: {}, truncate: {}", pflags.contains(OpenFlags::READ), pflags.contains(OpenFlags::WRITE), pflags.contains(OpenFlags::APPEND), pflags.contains(OpenFlags::CREATE), pflags.contains(OpenFlags::TRUNCATE)); + let path = format!("{}{}", self.jail_dir, filename); + if pflags.contains(OpenFlags::EXCLUDE) && fs::metadata(&path).await.is_ok() { + return Err(StatusCode::Failure) + } let mut options = OpenOptions::new(); options .read(pflags.contains(OpenFlags::READ)) @@ -69,7 +74,7 @@ impl SftpHandler for SftpSession { .append(pflags.contains(OpenFlags::APPEND)) .create(pflags.contains(OpenFlags::CREATE)) .truncate(pflags.contains(OpenFlags::TRUNCATE)); - match options.open(&filename).await { + match options.open(&path).await { Ok(file) => { self.handles.insert(filename.clone(), Handle::File(file)); Ok(SftpHandle { id, handle: filename }) @@ -133,8 +138,8 @@ impl SftpHandler for SftpSession { offset: u64, data: Vec, ) -> Result { + println!("write called, offset: {}, data: {:?}", offset, String::from_utf8(data.clone())); if let Handle::File(file) = self.handles.get_mut(&handle).unwrap() { - match file.seek(SeekFrom::Start(offset)).await { Ok(_) => { match file.write_all(&data).await { @@ -170,7 +175,7 @@ impl SftpHandler for SftpSession { path: String, ) -> Result { println!("opendir called: {}", path); - let path = format!("{}/{}", self.jail_dir, path); + let path = format!("{}{}", self.jail_dir, path); match fs::read_dir(&path).await { Ok(entries) => { self.handles.insert(path.clone(), Handle::Dir(entries)); @@ -247,7 +252,7 @@ impl SftpHandler for SftpSession { path: String, ) -> Result { println!("stat called: {}", path); - let path = format!("{}/{}", self.jail_dir, path); + let path = format!("{}{}", self.jail_dir, path); match fs::metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { size: Some(metadata.size()), @@ -270,7 +275,7 @@ impl SftpHandler for SftpSession { path: String, ) -> Result { println!("lstat called: {}", path); - let path = format!("{}/{}", self.jail_dir, path); + let path = format!("{}{}", self.jail_dir, path); match fs::symlink_metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { size: Some(metadata.size()), @@ -293,8 +298,22 @@ impl SftpHandler for SftpSession { id: u32, handle: String, ) -> Result { - println!("fstat called"); - self.stat(id, handle).await + println!("fstat called: {}", handle); + if let Handle::File(file) = self.handles.get(&handle).unwrap() { + let metadata = file.metadata().await.unwrap(); + Ok(Attrs { id, attrs: FileAttributes { + size: Some(metadata.size()), + permissions: Some(metadata.mode()), + atime: Some(metadata.atime() as u32), + mtime: Some(metadata.mtime() as u32), + ..Default::default() + }}) + } + else { + println!("handle is not a filehandle"); + Err(StatusCode::Failure) + } + } async fn remove( @@ -303,7 +322,7 @@ impl SftpHandler for SftpSession { filename: String, ) -> Result { println!("remove called: {}", filename); - let filename = format!("{}/{}", self.jail_dir, filename); + let filename = format!("{}{}", self.jail_dir, filename); match fs::remove_file(filename).await { Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), Err(e) => { @@ -326,7 +345,7 @@ impl SftpHandler for SftpSession { _attrs: FileAttributes, ) -> Result { println!("mkdir called: {}", path); - let path = format!("{}/{}", self.jail_dir, path); + let path = format!("{}{}", self.jail_dir, path); match fs::create_dir(&path).await { Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), Err(e) => { @@ -348,7 +367,7 @@ impl SftpHandler for SftpSession { path: String, ) -> Result { println!("rmdir called: {}", path); - let path = format!("{}/{}", self.jail_dir, path); + let path = format!("{}{}", self.jail_dir, path); match fs::remove_dir(path).await { Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), Err(e) => { @@ -371,8 +390,8 @@ impl SftpHandler for SftpSession { newpath: String, ) -> Result { println!("rename called from: {}, to: {}", oldpath, newpath); - let oldpath = format!("{}/{}", self.jail_dir, oldpath); - let newpath = format!("{}/{}", self.jail_dir, newpath); + let oldpath = format!("{}{}", self.jail_dir, oldpath); + let newpath = format!("{}{}", self.jail_dir, newpath); match fs::rename(oldpath, newpath).await { Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), From 2ce96ae74c842ee536440cc182ca2e8dc1e5d194 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Sun, 6 Jul 2025 16:51:27 +0500 Subject: [PATCH 09/10] added a macro --- src/sftp.rs | 89 ++++++++++++++--------------------------------------- 1 file changed, 23 insertions(+), 66 deletions(-) diff --git a/src/sftp.rs b/src/sftp.rs index d98eeae..126af41 100644 --- a/src/sftp.rs +++ b/src/sftp.rs @@ -6,6 +6,24 @@ use russh_sftp::{protocol::{Attrs, Data, File, FileAttributes, Handle as SftpHan use tokio::{fs::{self, OpenOptions, ReadDir}, io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}}; +macro_rules! match_expr { + ($match:expr, $err_msg:literal, $id:ident) => { + match $match { + Ok(()) => Ok(Status { $id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), + Err(e) => { + println!($err_msg, e); + match e.kind() { + ErrorKind::NotFound => Ok(Status { $id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::PermissionDenied => Ok(Status { $id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::ConnectionReset => Ok(Status { $id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), + ErrorKind::NotConnected => Ok(Status { $id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), + _ => Ok(Status { $id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) + } + } + } + }; +} + enum Handle { Dir(ReadDir), File(fs::File) @@ -206,10 +224,6 @@ impl SftpHandler for SftpSession { longname: longname, attrs: FileAttributes { size: Some(metadata.size()), - // uid: Some(metadata.uid()), - // user: None, - // gid: Some(metadata.gid()), - // group: None, permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), @@ -256,10 +270,6 @@ impl SftpHandler for SftpSession { match fs::metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { size: Some(metadata.size()), - // uid: Some(metadata.uid()), - // user: None, - // gid: Some(metadata.gid()), - // group: None, permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), @@ -279,10 +289,6 @@ impl SftpHandler for SftpSession { match fs::symlink_metadata(path).await { Ok(metadata) => Ok(Attrs { id, attrs: FileAttributes { size: Some(metadata.size()), - // uid: Some(metadata.uid()), - // user: None, - // gid: Some(metadata.gid()), - // group: None, permissions: Some(metadata.mode()), atime: Some(metadata.atime() as u32), mtime: Some(metadata.mtime() as u32), @@ -322,20 +328,8 @@ impl SftpHandler for SftpSession { filename: String, ) -> Result { println!("remove called: {}", filename); - let filename = format!("{}{}", self.jail_dir, filename); - match fs::remove_file(filename).await { - Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), - Err(e) => { - println!("error removing file: {}", e); - match e.kind() { - ErrorKind::NotFound => Ok(Status { id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), - _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) - } - } - } + let path = format!("{}{}", self.jail_dir, filename); + match_expr!(fs::remove_file(path).await, "error removing file: {}", id) } async fn mkdir( @@ -346,19 +340,7 @@ impl SftpHandler for SftpSession { ) -> Result { println!("mkdir called: {}", path); let path = format!("{}{}", self.jail_dir, path); - match fs::create_dir(&path).await { - Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), - Err(e) => { - println!("error removing file: {}", e); - match e.kind() { - ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), - _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) - } - } - } - + match_expr!(fs::create_dir(path).await, "error creating dir: {}", id) } async fn rmdir( @@ -368,19 +350,7 @@ impl SftpHandler for SftpSession { ) -> Result { println!("rmdir called: {}", path); let path = format!("{}{}", self.jail_dir, path); - match fs::remove_dir(path).await { - Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), - Err(e) => { - println!("error removing file: {}", e); - match e.kind() { - ErrorKind::NotFound => Ok(Status { id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), - _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) - } - } - } + match_expr!(fs::remove_dir(path).await, "error removing file: {}", id) } async fn rename( @@ -392,20 +362,7 @@ impl SftpHandler for SftpSession { println!("rename called from: {}, to: {}", oldpath, newpath); let oldpath = format!("{}{}", self.jail_dir, oldpath); let newpath = format!("{}{}", self.jail_dir, newpath); - - match fs::rename(oldpath, newpath).await { - Ok(()) => Ok(Status { id, status_code: StatusCode::Ok, error_message: "Ok".to_string(), language_tag: "en-US".to_string() }), - Err(e) => { - println!("error removing file: {}", e); - match e.kind() { - ErrorKind::NotFound => Ok(Status { id, status_code: StatusCode::NoSuchFile, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::PermissionDenied => Ok(Status { id, status_code: StatusCode::PermissionDenied, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::ConnectionReset => Ok(Status { id, status_code: StatusCode::ConnectionLost, error_message: e.to_string(), language_tag: "en-US".to_string() }), - ErrorKind::NotConnected => Ok(Status { id, status_code: StatusCode::NoConnection, error_message: e.to_string(), language_tag: "en-US".to_string() }), - _ => Ok(Status { id, status_code: StatusCode::Failure, error_message: e.to_string(), language_tag: "en-US".to_string() }) - } - } - } + match_expr!(fs::rename(oldpath, newpath).await, "error renaming file: {}", id) } } From c717f9f4f6585b18f2268cb6872d789ca76e7099 Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Mon, 7 Jul 2025 07:46:50 +0500 Subject: [PATCH 10/10] simple auth using sqlite --- .gitignore | 2 + Cargo.lock | 915 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + auth.db | Bin 0 -> 16384 bytes src/main.rs | 49 ++- 5 files changed, 941 insertions(+), 26 deletions(-) create mode 100644 auth.db diff --git a/.gitignore b/.gitignore index ea8c4bf..2eac0a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +sftptest +sftptest.pub diff --git a/Cargo.lock b/Cargo.lock index 9301c3a..665e621 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -101,6 +107,15 @@ dependencies = [ "password-hash", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -119,7 +134,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -128,6 +143,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.8.0" @@ -274,6 +295,15 @@ dependencies = [ "inout", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -315,6 +345,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -324,6 +369,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.4" @@ -429,6 +489,23 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "ecdsa" version = "0.16.9" @@ -468,6 +545,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -501,6 +587,34 @@ dependencies = [ "syn", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "ff" version = "0.13.1" @@ -527,6 +641,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "flurry" version = "0.5.2" @@ -547,9 +672,25 @@ dependencies = [ "regex", "russh", "russh-sftp", + "sqlx", "tokio", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.31" @@ -592,6 +733,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -690,6 +842,32 @@ dependencies = [ "subtle", ] +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.5.2" @@ -759,6 +937,123 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inout" version = "0.1.4" @@ -797,6 +1092,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "js-sys" version = "0.3.77" @@ -828,6 +1129,23 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.13" @@ -844,6 +1162,16 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "md5" version = "0.7.0" @@ -1031,6 +1359,12 @@ dependencies = [ "windows", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.4" @@ -1051,7 +1385,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1084,6 +1418,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1134,6 +1474,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "poly1305" version = "0.8.0" @@ -1157,6 +1503,15 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1419,6 +1774,12 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "salsa20" version = "0.10.2" @@ -1491,6 +1852,30 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1549,6 +1934,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -1565,6 +1953,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -1576,6 +1967,194 @@ dependencies = [ "der", ] +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.12", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.12", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.12", + "tracing", + "url", +] + [[package]] name = "ssh-cipher" version = "0.2.0" @@ -1605,6 +2184,23 @@ dependencies = [ "sha2", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1622,6 +2218,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1671,6 +2278,31 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.45.1" @@ -1700,6 +2332,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -1713,18 +2356,71 @@ dependencies = [ "tokio", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "universal-hash" version = "0.5.1" @@ -1735,6 +2431,29 @@ dependencies = [ "subtle", ] +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1747,6 +2466,12 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1828,6 +2553,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1857,7 +2592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core 0.58.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1870,7 +2605,7 @@ dependencies = [ "windows-interface 0.58.0", "windows-result 0.2.0", "windows-strings 0.1.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1942,7 +2677,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1961,7 +2696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result 0.2.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1973,13 +2708,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1988,7 +2732,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1997,28 +2756,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2031,30 +2808,84 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.26" @@ -2075,8 +2906,62 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 4f47fdb..b1af28b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ regex = "1.11.1" russh = "0.52.1" russh-sftp = "2.1.1" tokio = { version = "1.45.1", features = ["full"] } +sqlx = { version = "0.8.6", features = [ "runtime-tokio", "postgres", "sqlite", "mysql" ] } diff --git a/auth.db b/auth.db new file mode 100644 index 0000000000000000000000000000000000000000..661bfb66d78be1ff989909d0c5c1c0a868bd7892 GIT binary patch literal 16384 zcmeI$F;Buk6bJA-N>oTl-5Mrulqg1MKp9LdwNYX%2-OHnEl>l2g3vZ78=pnLj$g+( zdM#nb&7l96yS~1=wCVk}*T${h_Z{{$p1QWrWU@;rCFhJ0LIghJe6E7>n^hg=uwEAe zIhwwu#4i#P2Sgl*AL|~#J_H~D0SG_<0uX=z1Rwwb2tZ)N1s>_PBuR9!=iBX}6L`+l zi!Op%*A!D@rc%~57A@^3!!)v8hnd=g$!hh=jZ$y2Ypr>hd=A>fe#aU(ugguNYBJ+a z*OQq{Y)d+mDCv*7&Wkr0^3L9}1AiRVR`g~o7p1U&-w8dVCjKX#{D$M>^C18M2tWV= z5P$##AOHafKmY>&Ss=v^PsMk_2Lb{RfB*y_009U<00Izz00bZafnOA8(Rg~1o!X=B z*!8?#*6HR8g> +} impl Server for SftpServer { type Handler = SshSession; fn new_client(&mut self, _peer_addr: Option) -> Self::Handler { - SshSession{ channel: None, user: None } + let session_pool = self.pool.clone(); + SshSession { channel: None, user: None, pool: session_pool } } } struct SshSession { channel: Option>, - user: Option + user: Option, + pool: Arc> } impl SshHandler for SshSession { @@ -27,18 +32,35 @@ impl SshHandler for SshSession { user: &str, public_key: &PublicKey, ) -> Result { - let _ = public_key; self.user = Some(user.to_string()); - Ok(Auth::Accept) + + let row_res = sqlx::query("SELECT * FROM users WHERE username = ?") + .bind(user) + .fetch_one(&*self.pool).await; + + match row_res { + Ok(row) => { + let stored_key: String = row.get("public_key"); + let offered_key = public_key.to_string(); + if stored_key == offered_key { + Ok(Auth::Accept) + } + else { + Ok(Auth::reject()) + } + } + Err(e) => { + println!("User Not found: {}", e); + Ok(Auth::reject()) + } + } } async fn auth_publickey( &mut self, - user: &str, - public_key: &PublicKey, + _user: &str, + _public_key: &PublicKey, ) -> Result { - let _ = user; - let _ = public_key; Ok(Auth::Accept) } @@ -80,7 +102,12 @@ impl SshHandler for SshSession { #[tokio::main] -async fn main() { +async fn main() -> Result<(), sqlx::Error> { + + let pool = SqlitePoolOptions::new() + .max_connections(3) + .connect("sqlite:/home/rafayahmad/Stuff/Coding/Rust/flux-sftp/auth.db").await?; + let mut server = SftpServer { pool: Arc::new(pool) }; let config = russh::server::Config { auth_rejection_time: Duration::from_secs(3), @@ -90,7 +117,7 @@ async fn main() { ], ..Default::default() }; - let mut server = SftpServer; server.run_on_address(Arc::new(config), ("0.0.0.0", 2222)).await.unwrap(); + Ok(()) }