diff --git a/src/packet.rs b/src/packet.rs index f309162..3b20779 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -3,40 +3,63 @@ pub mod header; pub mod types; use crate::constants::SSH_FXP_INIT; +use crate::constants::SSH_FXP_REALPATH; use crate::constants::SSH_FXP_VERSION; +use crate::constants::SSH_FXP_NAME; use std::io::{BufReader, BufWriter, Read, Write}; use std::process::exit; pub fn dispatch_packet(packet: Packet) { let mut buff_stdout = BufWriter::new(std::io::stdout()); - if packet.packet_header.type_packet == SSH_FXP_INIT { - let init_packet_data: data::version::VersionData = - match data::version::VersionData::deserialize(&packet.data) { - Ok(ok) => ok, - Err(err) => panic!(err), - }; - eprintln!("The client version is {}.", init_packet_data.version); - if init_packet_data.version < 3 { - panic!("Unsupported version, minimal client version 3."); - } - let version_packet_data = data::version::VersionData { - version: 3, - extension_data: Vec::new(), + let mut response: Packet = match packet.packet_header.type_packet { + SSH_FXP_INIT => dispatch_version_request(packet), + SSH_FXP_REALPATH => dispatch_realpath_request(packet), + _ => panic!("{} not implemented.", packet.packet_header.type_packet), + }; + let serialized_response = response.serialize(); + eprintln!("Writing response."); + eprintln!("{:#?}", &serialized_response); + buff_stdout + .write(&serialized_response) + .expect("Unable to write to stdout."); + buff_stdout.flush().expect("Unable to flush stdout."); +} + +fn dispatch_realpath_request(packet: Packet) -> Packet { + let realpath_packet_data: data::realpath::RealpathData = + data::realpath::RealpathData::deserialize(&packet.data); + let path = crate::sftp::realpath(&realpath_packet_data.path); + let id = realpath_packet_data.id; + Packet { + packet_header: header::PacketHeader { + length: 0, + type_packet: SSH_FXP_NAME, + }, + data: crate::packet::data::name::NameData::new(id, &[path]).serialize(), + } +} + +fn dispatch_version_request(packet: Packet) -> Packet { + let init_packet_data: data::version::VersionData = + match data::version::VersionData::deserialize(&packet.data) { + Ok(ok) => ok, + Err(err) => panic!(err), }; - let mut response_packet = Packet { - packet_header: header::PacketHeader { - length: 0, - type_packet: SSH_FXP_VERSION, - }, - data: version_packet_data.serialize(), - }; - let serialized_response = response_packet.serialize(); - eprintln!("{:#?}", &serialized_response); - buff_stdout - .write(&serialized_response) - .expect("Unable to write to stdout."); - buff_stdout.flush().expect("Unable to flush stdout."); + eprintln!("The client version is {}.", init_packet_data.version); + if init_packet_data.version < 3 { + panic!("Unsupported version, minimal client version 3."); + } + let version_packet_data = data::version::VersionData { + version: 3, + extension_data: Vec::new(), + }; + Packet { + packet_header: header::PacketHeader { + length: 0, + type_packet: SSH_FXP_VERSION, + }, + data: version_packet_data.serialize(), } } diff --git a/src/packet/data.rs b/src/packet/data.rs index a6db76a..a38e915 100644 --- a/src/packet/data.rs +++ b/src/packet/data.rs @@ -1 +1,3 @@ pub mod version; +pub mod realpath; +pub mod name; diff --git a/src/packet/data/name.rs b/src/packet/data/name.rs new file mode 100644 index 0000000..f91dbe5 --- /dev/null +++ b/src/packet/data/name.rs @@ -0,0 +1,59 @@ +pub struct NameData { + id: u32, + count: u32, + file_descriptions: Vec, +} + +struct FileDescription { + filename: String, + longname: String, + attrs: u32, +} + +impl NameData { + pub fn new(id: u32, files: &[String]) -> NameData { + let count = files.len(); + let files: Vec = files + .iter() + .map(|a| FileDescription { + filename: a.to_string(), + longname: a.to_string(), + attrs: 0, + }) + .collect(); + NameData { + id: id, + count: count as u32, + file_descriptions: files, + } + } + pub fn serialize(&self) -> Vec { + let mut serialized_data: Vec = Vec::new(); + serialized_data.extend(&self.id.to_be_bytes()); + serialized_data.extend(&self.count.to_be_bytes()); + serialized_data.extend( + self.file_descriptions + .iter() + .map(|a| a.serialize()) + .collect::>>() + .concat(), + ); + serialized_data + } +} + +impl FileDescription { + fn serialize(&self) -> Vec { + let mut serialized_data: Vec = Vec::new(); + let filename = crate::packet::types::string::String { + content: self.filename.clone(), + }; + let longname = crate::packet::types::string::String { + content: self.longname.clone(), + }; + serialized_data.extend(filename.serialize()); + serialized_data.extend(longname.serialize()); + serialized_data.extend(&self.attrs.to_be_bytes()); + serialized_data + } +} diff --git a/src/packet/data/realpath.rs b/src/packet/data/realpath.rs new file mode 100644 index 0000000..763afc4 --- /dev/null +++ b/src/packet/data/realpath.rs @@ -0,0 +1,21 @@ +use std::io::Read; + +pub struct RealpathData { + pub id: u32, + pub path: String, +} + +impl RealpathData { + pub fn deserialize(mut u8_array_data: &[u8]) -> RealpathData { + if u8_array_data.len() < 5 { + panic!("Realpath data is expected to be bigger."); + } + let mut id: [u8; 4] = [0; 4]; + u8_array_data + .read(&mut id) + .expect("Failed to read id from realpath packet."); + let id = u32::from_be_bytes(id); + let path = crate::packet::types::string::String::deserialize(u8_array_data).content; + RealpathData { id: id, path: path } + } +} diff --git a/src/packet/types.rs b/src/packet/types.rs index 3bb9df5..d245b85 100644 --- a/src/packet/types.rs +++ b/src/packet/types.rs @@ -1 +1 @@ -pub mod str; +pub mod string; diff --git a/src/packet/types/str.rs b/src/packet/types/str.rs deleted file mode 100644 index df0eae3..0000000 --- a/src/packet/types/str.rs +++ /dev/null @@ -1,23 +0,0 @@ -use regex::Regex; -use std::io::BufRead; - -pub fn serialize(string_to_serialize: &str) -> Vec { - [string_to_serialize, "\0"].join("").as_bytes().to_vec() -} - -pub fn deserialize(bytes_to_deserialize: &[u8]) -> String { - Regex::new("\0$") - .expect("Unable to parse regex \\0$.") - .replace( - &String::from_utf8(bytes_to_deserialize.to_vec()) - .expect("Get a utf-8 encoded ftp client."), - "", - ) - .to_string() -} - -pub fn get_u8_array_c_string_from_u8_array(mut u8_array: &[u8]) -> Vec { - let mut return_array: Vec = Vec::new(); - u8_array.read_until(0, &mut return_array).expect("Could not read string from u8_array"); - return_array -} diff --git a/src/packet/types/string.rs b/src/packet/types/string.rs new file mode 100644 index 0000000..fab2f46 --- /dev/null +++ b/src/packet/types/string.rs @@ -0,0 +1,29 @@ +use std::io::Read; + +pub struct String { + pub content: std::string::String, +} + +impl String { + pub fn serialize(&self) -> Vec { + let mut serialized: Vec = Vec::new(); + let content = self.content.as_bytes(); + serialized.extend(&(content.len() as u32).to_be_bytes()); + serialized.extend(content); + serialized + } + + pub fn deserialize(mut to_serialize: &[u8]) -> String { + let mut size: [u8; 4] = [0; 4]; + to_serialize.read(&mut size).expect("Could not read string size."); + let size: u32 = u32::from_be_bytes(size); + let mut content: Vec = Vec::new(); + content.resize(size as usize, 0); + to_serialize.read(&mut content).expect("Could not read string."); + let content = match std::string::String::from_utf8(content.clone()) { + Ok(ok) => ok, + Err(err) => panic!("Error parsing {:#?} as utf-8: {}", content, err), + }; + String { content: content } + } +}