Add duplicate packet support
This commit is contained in:
parent
64f7e0f7ef
commit
0f241e5f05
6 changed files with 79 additions and 20 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -4,4 +4,4 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tftpd"
|
name = "tftpd"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tftpd"
|
name = "tftpd"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
authors = ["Altuğ Bakan <mail@alt.ug>"]
|
authors = ["Altuğ Bakan <mail@alt.ug>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Multithreaded TFTP server daemon"
|
description = "Multithreaded TFTP server daemon"
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ pub struct Config {
|
||||||
pub single_port: bool,
|
pub single_port: bool,
|
||||||
/// Refuse all write requests, making the server read-only. (default: false)
|
/// Refuse all write requests, making the server read-only. (default: false)
|
||||||
pub read_only: bool,
|
pub read_only: bool,
|
||||||
|
/// Duplicate all packets sent from the server. (default: 1)
|
||||||
|
pub duplicate_packets: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
@ -41,6 +43,7 @@ impl Config {
|
||||||
directory: env::current_dir().unwrap_or_else(|_| env::temp_dir()),
|
directory: env::current_dir().unwrap_or_else(|_| env::temp_dir()),
|
||||||
single_port: false,
|
single_port: false,
|
||||||
read_only: false,
|
read_only: false,
|
||||||
|
duplicate_packets: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
args.next();
|
args.next();
|
||||||
|
|
@ -88,9 +91,22 @@ impl Config {
|
||||||
println!(" -d, --directory <DIRECTORY>\tSet the listening port of the server (default: Current Working Directory)");
|
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!(" -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!(" -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");
|
println!(" -h, --help\t\t\tPrint help information");
|
||||||
process::exit(0);
|
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()),
|
invalid => return Err(format!("Invalid flag: {invalid}").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,4 +175,21 @@ mod tests {
|
||||||
)
|
)
|
||||||
.is_err());
|
.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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ pub struct Server {
|
||||||
directory: PathBuf,
|
directory: PathBuf,
|
||||||
single_port: bool,
|
single_port: bool,
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
|
duplicate_packets: u8,
|
||||||
largest_block_size: usize,
|
largest_block_size: usize,
|
||||||
clients: HashMap<SocketAddr, Sender<Packet>>,
|
clients: HashMap<SocketAddr, Sender<Packet>>,
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +47,7 @@ impl Server {
|
||||||
directory: config.directory.clone(),
|
directory: config.directory.clone(),
|
||||||
single_port: config.single_port,
|
single_port: config.single_port,
|
||||||
read_only: config.read_only,
|
read_only: config.read_only,
|
||||||
|
duplicate_packets: config.duplicate_packets,
|
||||||
largest_block_size: DEFAULT_BLOCK_SIZE,
|
largest_block_size: DEFAULT_BLOCK_SIZE,
|
||||||
clients: HashMap::new(),
|
clients: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
@ -177,6 +179,7 @@ impl Server {
|
||||||
worker_options.block_size,
|
worker_options.block_size,
|
||||||
worker_options.timeout,
|
worker_options.timeout,
|
||||||
worker_options.window_size,
|
worker_options.window_size,
|
||||||
|
self.duplicate_packets,
|
||||||
);
|
);
|
||||||
worker.send()
|
worker.send()
|
||||||
}
|
}
|
||||||
|
|
@ -234,6 +237,7 @@ impl Server {
|
||||||
worker_options.block_size,
|
worker_options.block_size,
|
||||||
worker_options.timeout,
|
worker_options.timeout,
|
||||||
worker_options.window_size,
|
worker_options.window_size,
|
||||||
|
self.duplicate_packets,
|
||||||
);
|
);
|
||||||
worker.receive()
|
worker.receive()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,10 +119,10 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, OpenOptions},
|
fs::{self, OpenOptions},
|
||||||
io::{Seek, Write},
|
io::Write,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DIR_NAME: &str = "tmp";
|
const DIR_NAME: &str = "target/test";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fills_and_removes_from_window() {
|
fn fills_and_removes_from_window() {
|
||||||
|
|
@ -131,7 +131,9 @@ mod tests {
|
||||||
let mut file = initialize(FILE_NAME);
|
let mut file = initialize(FILE_NAME);
|
||||||
file.write_all(b"Hello, world!").unwrap();
|
file.write_all(b"Hello, world!").unwrap();
|
||||||
file.flush().unwrap();
|
file.flush().unwrap();
|
||||||
file.rewind().unwrap();
|
drop(file);
|
||||||
|
|
||||||
|
file = open(FILE_NAME);
|
||||||
|
|
||||||
let mut window = Window::new(2, 5, file);
|
let mut window = Window::new(2, 5, file);
|
||||||
window.fill().unwrap();
|
window.fill().unwrap();
|
||||||
|
|
@ -204,6 +206,17 @@ mod tests {
|
||||||
.unwrap()
|
.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) {
|
fn clean(file_name: &str) {
|
||||||
let file_name = DIR_NAME.to_string() + "/" + file_name;
|
let file_name = DIR_NAME.to_string() + "/" + file_name;
|
||||||
fs::remove_file(file_name).unwrap();
|
fs::remove_file(file_name).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ const TIMEOUT_BUFFER: Duration = Duration::from_secs(1);
|
||||||
/// 512,
|
/// 512,
|
||||||
/// Duration::from_secs(1),
|
/// Duration::from_secs(1),
|
||||||
/// 1,
|
/// 1,
|
||||||
|
/// 1,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// worker.send().unwrap();
|
/// worker.send().unwrap();
|
||||||
|
|
@ -42,6 +43,7 @@ pub struct Worker<T: Socket + ?Sized> {
|
||||||
blk_size: usize,
|
blk_size: usize,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
windowsize: u16,
|
windowsize: u16,
|
||||||
|
duplicate_packets: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Socket + ?Sized> Worker<T> {
|
impl<T: Socket + ?Sized> Worker<T> {
|
||||||
|
|
@ -52,6 +54,7 @@ impl<T: Socket + ?Sized> Worker<T> {
|
||||||
blk_size: usize,
|
blk_size: usize,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
windowsize: u16,
|
windowsize: u16,
|
||||||
|
duplicate_packets: u8,
|
||||||
) -> Worker<T> {
|
) -> Worker<T> {
|
||||||
Worker {
|
Worker {
|
||||||
socket,
|
socket,
|
||||||
|
|
@ -59,6 +62,7 @@ impl<T: Socket + ?Sized> Worker<T> {
|
||||||
blk_size,
|
blk_size,
|
||||||
timeout,
|
timeout,
|
||||||
windowsize,
|
windowsize,
|
||||||
|
duplicate_packets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +140,7 @@ impl<T: Socket + ?Sized> Worker<T> {
|
||||||
let mut time = Instant::now() - (self.timeout + TIMEOUT_BUFFER);
|
let mut time = Instant::now() - (self.timeout + TIMEOUT_BUFFER);
|
||||||
loop {
|
loop {
|
||||||
if time.elapsed() >= self.timeout {
|
if time.elapsed() >= self.timeout {
|
||||||
send_window(&self.socket, &window, block_number)?;
|
self.send_window(&window, block_number)?;
|
||||||
time = Instant::now();
|
time = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -214,7 +218,8 @@ impl<T: Socket + ?Sized> Worker<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.empty()?;
|
window.empty()?;
|
||||||
self.socket.send(&Packet::Ack(block_number))?;
|
self.send_packet(&Packet::Ack(block_number))?;
|
||||||
|
|
||||||
if size < self.blk_size {
|
if size < self.blk_size {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
@ -222,15 +227,10 @@ impl<T: Socket + ?Sized> Worker<T> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn send_window<T: Socket>(
|
fn send_window(&self, window: &Window, mut block_num: u16) -> Result<(), Box<dyn Error>> {
|
||||||
socket: &T,
|
|
||||||
window: &Window,
|
|
||||||
mut block_num: u16,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
for frame in window.get_elements() {
|
for frame in window.get_elements() {
|
||||||
socket.send(&Packet::Data {
|
self.send_packet(&Packet::Data {
|
||||||
block_num,
|
block_num,
|
||||||
data: frame.to_vec(),
|
data: frame.to_vec(),
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -238,4 +238,13 @@ fn send_window<T: Socket>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_packet(&self, packet: &Packet) -> Result<(), Box<dyn Error>> {
|
||||||
|
for _ in 0..self.duplicate_packets {
|
||||||
|
self.socket.send(packet)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue