Add duplicate packet support

This commit is contained in:
Altuğ Bakan 2023-10-07 14:28:56 +02:00
parent 64f7e0f7ef
commit 0f241e5f05
6 changed files with 79 additions and 20 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]]
name = "tftpd"
version = "0.2.6"
version = "0.2.7"

View file

@ -1,6 +1,6 @@
[package]
name = "tftpd"
version = "0.2.6"
version = "0.2.7"
authors = ["Altuğ Bakan <mail@alt.ug>"]
edition = "2021"
description = "Multithreaded TFTP server daemon"

View file

@ -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 <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 <NUM>\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::<u8>()?;
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());
}
}

View file

@ -32,6 +32,7 @@ pub struct Server {
directory: PathBuf,
single_port: bool,
read_only: bool,
duplicate_packets: u8,
largest_block_size: usize,
clients: HashMap<SocketAddr, Sender<Packet>>,
}
@ -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()
}

View file

@ -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();

View file

@ -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<T: Socket + ?Sized> {
blk_size: usize,
timeout: Duration,
windowsize: u16,
duplicate_packets: u8,
}
impl<T: Socket + ?Sized> Worker<T> {
@ -52,6 +54,7 @@ impl<T: Socket + ?Sized> Worker<T> {
blk_size: usize,
timeout: Duration,
windowsize: u16,
duplicate_packets: u8,
) -> Worker<T> {
Worker {
socket,
@ -59,6 +62,7 @@ impl<T: Socket + ?Sized> Worker<T> {
blk_size,
timeout,
windowsize,
duplicate_packets,
}
}
@ -136,7 +140,7 @@ impl<T: Socket + ?Sized> Worker<T> {
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<T: Socket + ?Sized> Worker<T> {
}
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<T: Socket + ?Sized> Worker<T> {
Ok(())
}
}
fn send_window<T: Socket>(
socket: &T,
window: &Window,
mut block_num: u16,
) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
for _ in 0..self.duplicate_packets {
self.socket.send(packet)?;
}
Ok(())
}
}