From 0f241e5f054b4c01a0faacc78c2409774d56f1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Altu=C4=9F=20Bakan?= Date: Sat, 7 Oct 2023 14:28:56 +0200 Subject: [PATCH] Add duplicate packet support --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/config.rs | 33 +++++++++++++++++++++++++++++++++ src/server.rs | 4 ++++ src/window.rs | 19 ++++++++++++++++--- src/worker.rs | 39 ++++++++++++++++++++++++--------------- 6 files changed, 79 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3f5a95..68b782a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "tftpd" -version = "0.2.6" +version = "0.2.7" diff --git a/Cargo.toml b/Cargo.toml index 80acd8b..256f158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tftpd" -version = "0.2.6" +version = "0.2.7" authors = ["Altuğ Bakan "] edition = "2021" description = "Multithreaded TFTP server daemon" diff --git a/src/config.rs b/src/config.rs index 92e7038..0a67240 100644 --- a/src/config.rs +++ b/src/config.rs @@ -29,6 +29,8 @@ pub struct Config { pub single_port: bool, /// Refuse all write requests, making the server read-only. (default: false) pub read_only: bool, + /// Duplicate all packets sent from the server. (default: 1) + pub duplicate_packets: u8, } impl Config { @@ -41,6 +43,7 @@ impl Config { directory: env::current_dir().unwrap_or_else(|_| env::temp_dir()), single_port: false, read_only: false, + duplicate_packets: 1, }; args.next(); @@ -88,9 +91,22 @@ impl Config { println!(" -d, --directory \tSet the listening port of the server (default: Current Working Directory)"); println!(" -s, --single-port\t\tUse a single port for both sending and receiving (default: false)"); println!(" -r, --read-only\t\tRefuse all write requests, making the server read-only (default: false)"); + println!(" --duplicate-packets \tDuplicate all packets sent from the server (default: 1)"); println!(" -h, --help\t\t\tPrint help information"); process::exit(0); } + "--duplicate-packets" => { + if let Some(duplicate_packets_str) = args.next() { + let duplicate_packets = duplicate_packets_str.parse::()?; + if duplicate_packets < 1 { + return Err("Duplicate packets must be greater than 0".into()); + } + config.duplicate_packets = duplicate_packets; + } else { + return Err("Missing duplicate packets after flag".into()); + } + } + invalid => return Err(format!("Invalid flag: {invalid}").into()), } } @@ -159,4 +175,21 @@ mod tests { ) .is_err()); } + + #[test] + fn returns_error_on_invalid_duplicate_packets() { + assert!(Config::new( + ["/", "--duplicate-packets", "0"] + .iter() + .map(|s| s.to_string()), + ) + .is_err()); + + assert!(Config::new( + ["/", "--duplicate-packets", "-1"] + .iter() + .map(|s| s.to_string()), + ) + .is_err()); + } } diff --git a/src/server.rs b/src/server.rs index 84a1adb..436de8e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -32,6 +32,7 @@ pub struct Server { directory: PathBuf, single_port: bool, read_only: bool, + duplicate_packets: u8, largest_block_size: usize, clients: HashMap>, } @@ -46,6 +47,7 @@ impl Server { directory: config.directory.clone(), single_port: config.single_port, read_only: config.read_only, + duplicate_packets: config.duplicate_packets, largest_block_size: DEFAULT_BLOCK_SIZE, clients: HashMap::new(), }; @@ -177,6 +179,7 @@ impl Server { worker_options.block_size, worker_options.timeout, worker_options.window_size, + self.duplicate_packets, ); worker.send() } @@ -234,6 +237,7 @@ impl Server { worker_options.block_size, worker_options.timeout, worker_options.window_size, + self.duplicate_packets, ); worker.receive() } diff --git a/src/window.rs b/src/window.rs index 88efe73..cac7aca 100644 --- a/src/window.rs +++ b/src/window.rs @@ -119,10 +119,10 @@ mod tests { use super::*; use std::{ fs::{self, OpenOptions}, - io::{Seek, Write}, + io::Write, }; - const DIR_NAME: &str = "tmp"; + const DIR_NAME: &str = "target/test"; #[test] fn fills_and_removes_from_window() { @@ -131,7 +131,9 @@ mod tests { let mut file = initialize(FILE_NAME); file.write_all(b"Hello, world!").unwrap(); file.flush().unwrap(); - file.rewind().unwrap(); + drop(file); + + file = open(FILE_NAME); let mut window = Window::new(2, 5, file); window.fill().unwrap(); @@ -204,6 +206,17 @@ mod tests { .unwrap() } + fn open(file_name: &str) -> File { + let file_name = DIR_NAME.to_string() + "/" + file_name; + + OpenOptions::new() + .read(true) + .append(true) + .create(true) + .open(file_name) + .unwrap() + } + fn clean(file_name: &str) { let file_name = DIR_NAME.to_string() + "/" + file_name; fs::remove_file(file_name).unwrap(); diff --git a/src/worker.rs b/src/worker.rs index f302a74..3e39e83 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -32,6 +32,7 @@ const TIMEOUT_BUFFER: Duration = Duration::from_secs(1); /// 512, /// Duration::from_secs(1), /// 1, +/// 1, /// ); /// /// worker.send().unwrap(); @@ -42,6 +43,7 @@ pub struct Worker { blk_size: usize, timeout: Duration, windowsize: u16, + duplicate_packets: u8, } impl Worker { @@ -52,6 +54,7 @@ impl Worker { blk_size: usize, timeout: Duration, windowsize: u16, + duplicate_packets: u8, ) -> Worker { Worker { socket, @@ -59,6 +62,7 @@ impl Worker { blk_size, timeout, windowsize, + duplicate_packets, } } @@ -136,7 +140,7 @@ impl Worker { let mut time = Instant::now() - (self.timeout + TIMEOUT_BUFFER); loop { if time.elapsed() >= self.timeout { - send_window(&self.socket, &window, block_number)?; + self.send_window(&window, block_number)?; time = Instant::now(); } @@ -214,7 +218,8 @@ impl Worker { } window.empty()?; - self.socket.send(&Packet::Ack(block_number))?; + self.send_packet(&Packet::Ack(block_number))?; + if size < self.blk_size { break; }; @@ -222,20 +227,24 @@ impl Worker { Ok(()) } -} -fn send_window( - socket: &T, - window: &Window, - mut block_num: u16, -) -> Result<(), Box> { - for frame in window.get_elements() { - socket.send(&Packet::Data { - block_num, - data: frame.to_vec(), - })?; - block_num = block_num.wrapping_add(1); + fn send_window(&self, window: &Window, mut block_num: u16) -> Result<(), Box> { + for frame in window.get_elements() { + self.send_packet(&Packet::Data { + block_num, + data: frame.to_vec(), + })?; + block_num = block_num.wrapping_add(1); + } + + Ok(()) } - Ok(()) + fn send_packet(&self, packet: &Packet) -> Result<(), Box> { + for _ in 0..self.duplicate_packets { + self.socket.send(packet)?; + } + + Ok(()) + } }