Add work on windowsize and run clippy
This commit is contained in:
parent
9e83591bae
commit
361d077d1b
8 changed files with 106 additions and 53 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -4,4 +4,4 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tftpd"
|
name = "tftpd"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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
43
src/window.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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",
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue