diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0e77fda --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "fake_sftp" +version = "0.1.0" +authors = ["sergiotarxz "] +edition = "2018" +include = [ "lib/**/*.php" ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bincode = "1.3.1" +serde = { version = "1.0.63", features = [ "derive" ] } diff --git a/lib/a.php b/lib/a.php new file mode 100644 index 0000000..93b9c24 --- /dev/null +++ b/lib/a.php @@ -0,0 +1,3 @@ + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..04d81e1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,184 @@ +use std::convert::TryInto; +use std::io::{stdin, BufReader, BufWriter, Read, Write}; +use std::process::Command; +use std::vec::Vec; + +const SSH_FXP_INIT: u8 = 1; +const SSH_FXP_VERSION: u8 = 2; +const SSH_FXP_OPEN: u8 = 3; +const SSH_FXP_CLOSE: u8 = 4; +const SSH_FXP_READ: u8 = 5; +const SSH_FXP_WRITE: u8 = 6; +const SSH_FXP_LSTAT: u8 = 7; +const SSH_FXP_FSTAT: u8 = 8; +const SSH_FXP_SETSTAT: u8 = 9; +const SSH_FXP_FSETSTAT: u8 = 10; +const SSH_FXP_OPENDIR: u8 = 11; +const SSH_FXP_READDIR: u8 = 12; +const SSH_FXP_REMOVE: u8 = 13; +const SSH_FXP_MKDIR: u8 = 14; +const SSH_FXP_RMDIR: u8 = 15; +const SSH_FXP_REALPATH: u8 = 16; +const SSH_FXP_STAT: u8 = 17; +const SSH_FXP_RENAME: u8 = 18; +const SSH_FXP_READLINK: u8 = 19; +const SSH_FXP_SYMLINK: u8 = 20; +const SSH_FXP_STATUS: u8 = 101; +const SSH_FXP_HANDLE: u8 = 102; +const SSH_FXP_DATA: u8 = 103; +const SSH_FXP_NAME: u8 = 104; +const SSH_FXP_ATTRS: u8 = 105; +const SSH_FXP_EXTENDED: u8 = 200; +const SSH_FXP_EXTENDED_REPLY: u8 = 201; +const NEXTCLOUD_PATH: &str = "/var/www/nextcloud.sergiotarxz.freemyip.com/htdocs/"; + +fn main() { + eprintln!("{}", execute_php("echo \"hola rusty mundo\";")); + loop { + let packet = Packet::read_packet(); + dispatch_packet(packet) + } +} + +fn execute_php(command: &str) -> String { + let output = Command::new("php") + .args(&["-r", command]) + .output() + .expect(""); + let mut buff_stderr = BufWriter::new(std::io::stderr()); + buff_stderr.write(&output.stderr); + buff_stderr.flush(); + return String::from_utf8(output.stdout).expect("Unable to parse php response"); +} + +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: VersionPacketData = match VersionPacketData::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 = VersionPacketData { + version: 3, + extension_data: Vec::new(), + }; + let mut response_packet = Packet { + packet_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); + buff_stdout.flush(); + } +} + +impl VersionPacketData { + fn deserialize(x: &[u8]) -> Result { + if x.len() < 4 { + return Err("Error parsing version from init packet header.".to_string()); + } else { + let mut u32_byte_array: [u8; 4] = [0; 4]; + let mut extension_data = Vec::new(); + u32_byte_array.copy_from_slice(&x[0..4]); + if x.len() > 4 { + extension_data.extend(&x[4..]); + } + return Ok(VersionPacketData { + version: u32::from_be_bytes(u32_byte_array), + extension_data: extension_data, + }); + } + } + fn serialize(&self) -> Vec { + let mut return_bytes = Vec::new(); + let u32_byte_array: [u8; 4] = self.version.to_be_bytes(); + return_bytes.extend(&u32_byte_array); + if self.extension_data.len() > 0 { + return_bytes.extend(&self.extension_data); + } + return_bytes + } +} + +impl Packet { + fn read_packet() -> Packet { + let mut packet_header: [u8; 5] = [0; 5]; + let mut buff_stdin = BufReader::new(std::io::stdin()); + match buff_stdin.read(&mut packet_header) { + Ok(ok) => ok, + Err(err) => panic!("Could not fetch packet: {}", err), + }; + eprintln!("{:#?}", packet_header); + let packet_header: PacketHeader = PacketHeader::deserialize(&packet_header); + eprintln!( + "Packet length {} and type {}.", + packet_header.length, packet_header.type_packet + ); + let mut data: Vec = Vec::new(); + data.resize(packet_header.length as usize - 1, 0); + match buff_stdin.read(&mut data) { + Ok(ok) => ok, + Err(err) => panic!("Could not fetch data: {}", err), + }; + eprintln!("{:#?}", data); + Packet::deserialize(packet_header, data) + } + fn deserialize(packet_header: PacketHeader, data: Vec) -> Packet { + Packet { + packet_header: packet_header, + data: data, + } + } + fn serialize(&mut self) -> Vec { + self.packet_header.length = (self.data.len() + 1) as u32; + let mut return_bytes = Vec::new(); + return_bytes.extend(&self.packet_header.serialize()); + return_bytes.extend(&self.data); + return_bytes + } +} + +struct PacketHeader { + length: u32, + type_packet: u8, +} +impl PacketHeader { + fn deserialize(x: &[u8; 5]) -> PacketHeader { + let mut u32_byte_array: [u8; 4] = [0; 4]; + u32_byte_array.copy_from_slice(&x[0..4]); + PacketHeader { + length: u32::from_be_bytes(u32_byte_array), + type_packet: x[4], + } + } + fn serialize(&self) -> [u8; 5] { + let serializated_data: [u8; 5] = [0; 5]; + let length = self.length.to_be_bytes(); + let type_packet = self.type_packet; + let mut return_bytes = length.to_vec(); + return_bytes.extend(&[type_packet][..]); + let boxed_return_bytes: Box<[u8; 5]> = return_bytes + .into_boxed_slice() + .try_into() + .expect("Unable to serialize."); + *boxed_return_bytes + } +} + +struct Packet { + packet_header: PacketHeader, + data: Vec, +} +struct VersionPacketData { + version: u32, + extension_data: Vec, +}