From 32ed133e21287e808bd32406c23af99e03b343be Mon Sep 17 00:00:00 2001 From: einisto <79069176+einisto@users.noreply.github.com> Date: Mon, 11 Jul 2022 21:10:05 +0300 Subject: [PATCH] CLI args * Client: target IP, fileroot (defaults to "./output/", still requires fixing) * Server: host port (defaults to 8080), fileroot (defaults to "./data/", still requires fixing), buffersize (defaults to 8192/64Kb), local (flag to run only locally @localhost) --- Cargo.lock | 242 ++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 6 +- src/bin/client.rs | 43 ++++++-- src/bin/server.rs | 76 +++++++++++++-- 4 files changed, 339 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f63cf28..2b0be48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -14,6 +25,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.1.0" @@ -26,6 +43,72 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "3.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "const-sha1" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d" + +[[package]] +name = "fragilebyte" +version = "0.1.0" +dependencies = [ + "clap", + "local-ip-address", + "tokio", +] + +[[package]] +name = "hashbrown" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -35,12 +118,35 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "local-ip-address" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b143c6ef86e36328caa40a7578e95d1544aca8a1740235fd2b416a69441a5c7" +dependencies = [ + "libc", + "memalloc", + "neli", + "thiserror", + "windows", +] + [[package]] name = "lock_api" version = "0.4.7" @@ -60,6 +166,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memalloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" + [[package]] name = "memchr" version = "2.5.0" @@ -78,6 +190,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "neli" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508" +dependencies = [ + "byteorder", + "libc", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -94,6 +216,12 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac8b1a9b2518dc799a2271eff1688707eb315f0d4697aa6b0871369ca4c4da55" +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" + [[package]] name = "parking_lot" version = "0.12.1" @@ -123,6 +251,30 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.40" @@ -181,6 +333,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.98" @@ -192,6 +350,41 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio" version = "1.19.2" @@ -223,19 +416,18 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-socket-test" -version = "0.1.0" -dependencies = [ - "tokio", -] - [[package]] name = "unicode-ident" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -258,12 +450,32 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68088239696c06152844eadc03d262f088932cce50c67e4ace86e19d95e976fe" +dependencies = [ + "const-sha1", + "windows_gen", + "windows_macros", +] + [[package]] name = "windows-sys" version = "0.36.1" @@ -283,6 +495,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_gen" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf583322dc423ee021035b358e535015f7fd163058a31e2d37b99a939141121d" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -295,6 +513,16 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58acfb8832e9f707f8997bd161e537a1c1f603e60a5bd9c3cf53484fdcc998f3" +dependencies = [ + "syn", + "windows_gen", +] + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" diff --git a/Cargo.toml b/Cargo.toml index c631564..d8dcc86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [package] -name = "tokio-socket-test" +name = "fragilebyte" +author = "Arttu Einistö" +about = "TCP socket pair for file transfer, backend for https://github.com/einisto/leightbox" version = "0.1.0" edition = "2021" @@ -13,3 +15,5 @@ name = "client" [dependencies] tokio = { version = "1.19.2", features = ["full"] } +clap = { version = "3.2.8", features = ["derive"] } +local-ip-address = "0.4.4" diff --git a/src/bin/client.rs b/src/bin/client.rs index ef86662..f638b76 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,3 +1,4 @@ +use clap::Parser; use std::time::Duration; use tokio::{ fs::File, @@ -8,13 +9,22 @@ use tokio::{ // TODO: Remove panics/unwraps & add proper error handling -const BUFFERSIZE: usize = 8192; +#[derive(Debug, Parser)] +#[clap(author, about, version)] +struct Args { + #[clap(short = 't', long, value_parser)] + target: String, + #[clap(default_value = "./output/", short = 'f', long, value_parser)] + fileroot: String, +} #[tokio::main] async fn main() -> Result<(), Box> { - // TODO: Clap - let addr = "127.0.0.1:8080"; - let mut stream = TcpStream::connect(addr).await?; + let args = Args::parse(); + let addr = args.target; + let fileroot = args.fileroot; + + let mut stream = TcpStream::connect(addr.clone()).await?; println!("[+] Connecting to {}", addr); let (reader, writer) = stream.split(); @@ -26,11 +36,24 @@ async fn main() -> Result<(), Box> { loop { let bytes_read = reader.read_buf(&mut buf).await.unwrap(); if bytes_read == 0 { - println!("[-] No more bytes received, disconnecting from the server..."); + println!("[-] No more bytes received, closing connection"); break; } + // Receive buffersize + let buffersize = String::from_utf8(buf.clone()) + .unwrap() + .parse::() + .unwrap(); + println!("[+] Selected buffersize: {}", buffersize); + buf.clear(); + + // ACK buffersize + writer.write_all(b"ACK").await.unwrap(); + writer.flush().await.unwrap(); + // Receive file amount + let _bytes_read = reader.read_buf(&mut buf).await.unwrap(); let file_amount = String::from_utf8(buf.clone()) .unwrap() .parse::() @@ -66,8 +89,8 @@ async fn main() -> Result<(), Box> { writer.write_all(file.0.as_bytes()).await.unwrap(); writer.flush().await.unwrap(); - // Create file locally (./output/) - let output_path = String::from("./output/") + file.0.as_str(); + // Create file locally + let output_path = fileroot.clone() + file.0.as_str(); let output_file = File::create(output_path.clone()).await.unwrap(); println!("[+] New file: {}", output_path); @@ -75,10 +98,10 @@ async fn main() -> Result<(), Box> { // Receive the file itself let mut remaining_data = file.1; - let mut buf = [0u8; BUFFERSIZE]; + let mut buf = vec![0u8; buffersize]; while remaining_data != 0 { - if remaining_data >= BUFFERSIZE as u64 { + if remaining_data >= buffersize as u64 { let read_result = reader.read(&mut buf); match read_result.await { @@ -118,7 +141,7 @@ async fn main() -> Result<(), Box> { ); } - println!("[+] All finished, disconnecting..."); + println!("[+] All files finished, requesting connection termination"); writer.write_all(b"FIN").await.unwrap(); writer.flush().await.unwrap(); } diff --git a/src/bin/server.rs b/src/bin/server.rs index c5434a8..c7fe324 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,4 +1,10 @@ -use std::fs::read_dir; +use clap::Parser; +use local_ip_address::local_ip; +use std::{ + fs::read_dir, + net::{IpAddr, SocketAddr}, + str::FromStr, +}; use tokio::{ self, fs::File, @@ -8,18 +14,37 @@ use tokio::{ // TODO: Remove panics/unwraps & add proper error handling -const BUFFERSIZE: usize = 8192; +#[derive(Parser, Debug)] +#[clap(author, about, version)] +struct Args { + #[clap(default_value_t = 8080u16, short = 'p', long, value_parser = validate_port)] + port: u16, + #[clap(default_value = "./data/", short = 'f', long, value_parser)] + fileroot: String, + #[clap(default_value_t = 8192usize, short = 'b', long, value_parser = validate_buffersize)] + buffersize: usize, + #[clap(default_value_t = false, short = 'l', long, action)] + local: bool, +} #[tokio::main] async fn main() -> Result<(), Box> { - // TODO: Clap - let addr = "127.0.0.1:8080"; + let args = Args::parse(); + let addr = match args.local { + true => SocketAddr::new(IpAddr::from_str("127.0.0.1")?, args.port), + false => SocketAddr::new(local_ip()?, args.port), + }; + let listener = TcpListener::bind(addr).await?; println!("[+] Listening on {}", addr); loop { + let args = Args::parse(); + let buffersize = args.buffersize; + let fileroot = args.fileroot; + let (mut socket, addr) = listener.accept().await?; - println!("[+] New client: {}", addr); + println!("\n[+] New client: {}", addr); tokio::spawn(async move { let (reader, writer) = socket.split(); @@ -28,6 +53,21 @@ async fn main() -> Result<(), Box> { let mut vec_buf = Vec::new(); + // Send buffersize + writer + .write_all(buffersize.to_string().as_bytes()) + .await + .unwrap(); + writer.flush().await.unwrap(); + + // Read ACK + let _bytes_read = reader.read_buf(&mut vec_buf).await.unwrap(); + if String::from_utf8(vec_buf.clone()).unwrap() != "ACK" { + panic!("ACK not received (buffersize)"); + } else { + vec_buf.clear(); + } + let (metadata_list, file_amount) = get_metadata().await; // Send file amount @@ -70,12 +110,12 @@ async fn main() -> Result<(), Box> { break; } - let input_path = String::from("./data/") + msg.as_str(); + let input_path = fileroot.clone() + msg.as_str(); - println!("[+] File requested: {}", input_path); + println!("\n[+] File requested: {}", input_path); let mut file = File::open(input_path.clone()).await.unwrap(); let mut remaining_data = file.metadata().await.unwrap().len(); - let mut filebuf = [0u8; BUFFERSIZE]; + let mut filebuf = vec![0u8; buffersize]; while remaining_data != 0 { let read_result = file.read(&mut filebuf); @@ -95,7 +135,7 @@ async fn main() -> Result<(), Box> { if String::from_utf8(vec_buf.clone()).unwrap() != "ACK" { panic!("ACK not received (amount)"); } else { - println!("[+] File transfer successfully done\n"); + println!("[+] File transfer successfully done"); vec_buf.clear(); } } @@ -114,10 +154,26 @@ async fn get_metadata() -> (Vec<(String, u64)>, usize) { let file = File::open(filepath).await.unwrap(); let filesize = file.metadata().await.unwrap().len(); - metadata.push((filename, filesize)); + if filesize > 0 { + metadata.push((filename, filesize)); + } } let amount = metadata.len(); (metadata, amount) } + +fn validate_buffersize(value: &str) -> Result { + match value.parse::() { + Ok(n) => Ok(n), + Err(_) => Err(format!("Invalid buffersize: {}", value)), + } +} + +fn validate_port(value: &str) -> Result { + match value.parse::() { + Ok(n) => Ok(n), + Err(_) => Err(format!("Invalid port-number: {}", value)), + } +}