Add work on windowsize and run clippy

This commit is contained in:
altugbakan 2023-03-29 20:07:42 +03:00 committed by Altuğ Bakan
parent 9e83591bae
commit 361d077d1b
8 changed files with 106 additions and 53 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]] [[package]]
name = "tftpd" name = "tftpd"
version = "0.1.5" version = "0.1.6"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "tftpd" name = "tftpd"
version = "0.1.5" version = "0.1.6"
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"

View file

@ -7,6 +7,7 @@ This server implements [RFC 1350](https://www.rfc-editor.org/rfc/rfc1350), The T
- [RFC 2348](https://www.rfc-editor.org/rfc/rfc2348) Blocksize Option - [RFC 2348](https://www.rfc-editor.org/rfc/rfc2348) Blocksize Option
- [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Timeout Interval Option - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Timeout Interval Option
- [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Transfer Size Option - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Transfer Size Option
- [RFC 7440](https://www.rfc-editor.org/rfc/rfc7440) Windowsize Option
# Security # Security

View file

@ -8,6 +8,7 @@
//! - [RFC 2348](https://www.rfc-editor.org/rfc/rfc2348) Blocksize Option //! - [RFC 2348](https://www.rfc-editor.org/rfc/rfc2348) Blocksize Option
//! - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Timeout Interval Option //! - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Timeout Interval Option
//! - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Transfer Size Option //! - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Transfer Size Option
//! - [RFC 7440](https://www.rfc-editor.org/rfc/rfc7440) Windowsize Option
//! //!
//! # Security //! # Security
//! //!
@ -19,6 +20,7 @@ mod convert;
mod message; mod message;
mod packet; mod packet;
mod server; mod server;
mod window;
mod worker; mod worker;
pub use config::Config; pub use config::Config;
@ -30,4 +32,5 @@ pub use packet::OptionType;
pub use packet::Packet; pub use packet::Packet;
pub use packet::TransferOption; pub use packet::TransferOption;
pub use server::Server; pub use server::Server;
pub use window::Window;
pub use worker::Worker; pub use worker::Worker;

View file

@ -1,5 +1,5 @@
use crate::Convert; use crate::Convert;
use std::{error::Error, fmt}; use std::{error::Error, fmt, str::FromStr};
/// Packet `enum` represents the valid TFTP packet types. /// Packet `enum` represents the valid TFTP packet types.
/// ///
@ -127,7 +127,7 @@ impl Opcode {
/// Converts a [`u16`] to a [`u8`] array with 2 elements. /// Converts a [`u16`] to a [`u8`] array with 2 elements.
pub const fn as_bytes(self) -> [u8; 2] { pub const fn as_bytes(self) -> [u8; 2] {
return (self as u16).to_be_bytes(); (self as u16).to_be_bytes()
} }
} }
@ -199,14 +199,18 @@ impl OptionType {
OptionType::Timeout => "timeout", OptionType::Timeout => "timeout",
} }
} }
}
impl FromStr for OptionType {
type Err = &'static str;
/// Converts a [`str`] to an [`OptionType`]. /// Converts a [`str`] to an [`OptionType`].
pub fn from_str(value: &str) -> Result<Self, &'static str> { fn from_str(value: &str) -> Result<Self, &'static str> {
match value { match value {
"blksize" => Ok(OptionType::BlockSize), "blksize" => Ok(OptionType::BlockSize),
"tsize" => Ok(OptionType::TransferSize), "tsize" => Ok(OptionType::TransferSize),
"timeout" => Ok(OptionType::Timeout), "timeout" => Ok(OptionType::Timeout),
_ => Err("Invalid option type".into()), _ => Err("Invalid option type"),
} }
} }
} }
@ -263,7 +267,7 @@ impl ErrorCode {
/// Converts an [`ErrorCode`] to a [`u8`] array with 2 elements. /// Converts an [`ErrorCode`] to a [`u8`] array with 2 elements.
pub fn as_bytes(self) -> [u8; 2] { pub fn as_bytes(self) -> [u8; 2] {
return (self as u16).to_be_bytes(); (self as u16).to_be_bytes()
} }
} }
@ -360,7 +364,7 @@ fn serialize_error(code: &ErrorCode, msg: &String) -> Vec<u8> {
[ [
&Opcode::Error.as_bytes()[..], &Opcode::Error.as_bytes()[..],
&code.as_bytes()[..], &code.as_bytes()[..],
&msg.as_bytes()[..], msg.as_bytes(),
&[0x00], &[0x00],
] ]
.concat() .concat()
@ -384,9 +388,9 @@ mod tests {
fn parses_read_request() { fn parses_read_request() {
let buf = [ let buf = [
&Opcode::Rrq.as_bytes()[..], &Opcode::Rrq.as_bytes()[..],
&"test.png".as_bytes(), ("test.png".as_bytes()),
&[0x00], &[0x00],
&"octet".as_bytes(), ("octet".as_bytes()),
&[0x00], &[0x00],
] ]
.concat(); .concat();
@ -409,17 +413,17 @@ mod tests {
fn parses_read_request_with_options() { fn parses_read_request_with_options() {
let buf = [ let buf = [
&Opcode::Rrq.as_bytes()[..], &Opcode::Rrq.as_bytes()[..],
&"test.png".as_bytes(), ("test.png".as_bytes()),
&[0x00], &[0x00],
&"octet".as_bytes(), ("octet".as_bytes()),
&[0x00], &[0x00],
&OptionType::TransferSize.as_str().as_bytes(), (OptionType::TransferSize.as_str().as_bytes()),
&[0x00], &[0x00],
&"0".as_bytes(), ("0".as_bytes()),
&[0x00], &[0x00],
&OptionType::Timeout.as_str().as_bytes(), (OptionType::Timeout.as_str().as_bytes()),
&[0x00], &[0x00],
&"5".as_bytes(), ("5".as_bytes()),
&[0x00], &[0x00],
] ]
.concat(); .concat();
@ -456,9 +460,9 @@ mod tests {
fn parses_write_request() { fn parses_write_request() {
let buf = [ let buf = [
&Opcode::Wrq.as_bytes()[..], &Opcode::Wrq.as_bytes()[..],
&"test.png".as_bytes(), ("test.png".as_bytes()),
&[0x00], &[0x00],
&"octet".as_bytes(), ("octet".as_bytes()),
&[0x00], &[0x00],
] ]
.concat(); .concat();
@ -481,17 +485,17 @@ mod tests {
fn parses_write_request_with_options() { fn parses_write_request_with_options() {
let buf = [ let buf = [
&Opcode::Wrq.as_bytes()[..], &Opcode::Wrq.as_bytes()[..],
&"test.png".as_bytes(), ("test.png".as_bytes()),
&[0x00], &[0x00],
&"octet".as_bytes(), ("octet".as_bytes()),
&[0x00], &[0x00],
&OptionType::TransferSize.as_str().as_bytes(), (OptionType::TransferSize.as_str().as_bytes()),
&[0x00], &[0x00],
&"12341234".as_bytes(), ("12341234".as_bytes()),
&[0x00], &[0x00],
&OptionType::BlockSize.as_str().as_bytes(), (OptionType::BlockSize.as_str().as_bytes()),
&[0x00], &[0x00],
&"1024".as_bytes(), ("1024".as_bytes()),
&[0x00], &[0x00],
] ]
.concat(); .concat();

View file

@ -2,7 +2,7 @@ use crate::{Config, Message, Worker};
use crate::{ErrorCode, Packet, TransferOption}; use crate::{ErrorCode, Packet, TransferOption};
use std::error::Error; use std::error::Error;
use std::net::{SocketAddr, UdpSocket}; use std::net::{SocketAddr, UdpSocket};
use std::path::PathBuf; use std::path::{Path, PathBuf};
/// Server `struct` is used for handling incoming TFTP requests. /// Server `struct` is used for handling incoming TFTP requests.
/// ///
@ -79,11 +79,11 @@ impl Server {
fn handle_rrq( fn handle_rrq(
&self, &self,
filename: String, filename: String,
options: &mut Vec<TransferOption>, options: &mut [TransferOption],
to: &SocketAddr, to: &SocketAddr,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let file_path = &self.directory.join(&filename); let file_path = &self.directory.join(filename);
match check_file_exists(&file_path, &self.directory) { match check_file_exists(file_path, &self.directory) {
ErrorCode::FileNotFound => Message::send_error_to( ErrorCode::FileNotFound => Message::send_error_to(
&self.socket, &self.socket,
to, to,
@ -96,12 +96,15 @@ impl Server {
ErrorCode::AccessViolation, ErrorCode::AccessViolation,
"file access violation", "file access violation",
), ),
ErrorCode::FileExists => Ok(Worker::send( ErrorCode::FileExists => {
self.socket.local_addr()?, Worker::send(
*to, self.socket.local_addr()?,
file_path.to_path_buf(), *to,
options.to_vec(), file_path.to_path_buf(),
)), options.to_vec(),
);
Ok(())
}
_ => Err("unexpected error code when checking file".into()), _ => Err("unexpected error code when checking file".into()),
} }
} }
@ -109,11 +112,11 @@ impl Server {
fn handle_wrq( fn handle_wrq(
&self, &self,
filename: String, filename: String,
options: &mut Vec<TransferOption>, options: &mut [TransferOption],
to: &SocketAddr, to: &SocketAddr,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let file_path = &self.directory.join(&filename); let file_path = &self.directory.join(filename);
match check_file_exists(&file_path, &self.directory) { match check_file_exists(file_path, &self.directory) {
ErrorCode::FileExists => Message::send_error_to( ErrorCode::FileExists => Message::send_error_to(
&self.socket, &self.socket,
to, to,
@ -126,18 +129,21 @@ impl Server {
ErrorCode::AccessViolation, ErrorCode::AccessViolation,
"file access violation", "file access violation",
), ),
ErrorCode::FileNotFound => Ok(Worker::receive( ErrorCode::FileNotFound => {
self.socket.local_addr()?, Worker::receive(
*to, self.socket.local_addr()?,
file_path.to_path_buf(), *to,
options.to_vec(), file_path.to_path_buf(),
)), options.to_vec(),
);
Ok(())
}
_ => Err("unexpected error code when checking file".into()), _ => Err("unexpected error code when checking file".into()),
} }
} }
} }
fn check_file_exists(file: &PathBuf, directory: &PathBuf) -> ErrorCode { fn check_file_exists(file: &Path, directory: &PathBuf) -> ErrorCode {
if !validate_file_path(file, directory) { if !validate_file_path(file, directory) {
return ErrorCode::AccessViolation; return ErrorCode::AccessViolation;
} }
@ -149,7 +155,7 @@ fn check_file_exists(file: &PathBuf, directory: &PathBuf) -> ErrorCode {
ErrorCode::FileExists ErrorCode::FileExists
} }
fn validate_file_path(file: &PathBuf, directory: &PathBuf) -> bool { fn validate_file_path(file: &Path, directory: &PathBuf) -> bool {
!file.to_str().unwrap().contains("..") && file.ancestors().any(|a| a == directory) !file.to_str().unwrap().contains("..") && file.ancestors().any(|a| a == directory)
} }

43
src/window.rs Normal file
View file

@ -0,0 +1,43 @@
use std::{collections::VecDeque, error::Error, fs::File, io::Read};
pub struct Window {
elements: VecDeque<Vec<u8>>,
size: usize,
chunk_size: usize,
file: File,
}
impl Window {
pub fn new(size: usize, chunk_size: usize, file: File) -> Window {
Window {
elements: VecDeque::new(),
size,
chunk_size,
file,
}
}
pub fn fill(&mut self) -> Result<(), Box<dyn Error>> {
for _ in self.elements.len()..self.size {
let mut chunk = vec![0; self.chunk_size];
let size = self.file.read(&mut chunk)?;
self.elements.push_back(chunk);
if size != self.chunk_size {
break;
}
}
Ok(())
}
pub fn remove(&mut self, amount: usize) -> Result<(), Box<dyn Error>> {
if amount > self.elements.len() {
return Err("amount cannot be larger than size".into());
}
drop(self.elements.drain(0..amount));
Ok(())
}
}

View file

@ -180,7 +180,7 @@ fn receive_file(
}) => { }) => {
if received_block_number == block_number.wrapping_add(1) { if received_block_number == block_number.wrapping_add(1) {
block_number = received_block_number; block_number = received_block_number;
file.write(&data)?; file.write_all(&data)?;
size = data.len(); size = data.len();
break; break;
} }
@ -216,7 +216,7 @@ fn accept_request(
options: &Vec<TransferOption>, options: &Vec<TransferOption>,
work_type: &WorkType, work_type: &WorkType,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
if options.len() > 0 { if !options.is_empty() {
Message::send_oack(socket, options.to_vec())?; Message::send_oack(socket, options.to_vec())?;
} else if *work_type == WorkType::Receive { } else if *work_type == WorkType::Receive {
Message::send_ack(socket, 0)? Message::send_ack(socket, 0)?
@ -226,13 +226,9 @@ fn accept_request(
} }
fn check_response(socket: &UdpSocket) -> Result<(), Box<dyn Error>> { fn check_response(socket: &UdpSocket) -> Result<(), Box<dyn Error>> {
if let Packet::Ack(received_block_number) = Message::recv(&socket)? { if let Packet::Ack(received_block_number) = Message::recv(socket)? {
if received_block_number != 0 { if received_block_number != 0 {
Message::send_error( Message::send_error(socket, ErrorCode::IllegalOperation, "invalid oack response")?;
&socket,
ErrorCode::IllegalOperation,
"invalid oack response",
)?;
} }
} }