Finish implementation

This commit is contained in:
altugbakan 2023-03-11 17:23:18 +03:00
parent c345a5b531
commit 0df85e156d
7 changed files with 404 additions and 180 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]] [[package]]
name = "tftpd" name = "tftpd"
version = "0.0.0" version = "0.1.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "tftpd" name = "tftpd"
version = "0.0.0" version = "0.1.0"
authors = ["Altuğ Bakan <mail@alt.ug>"] authors = ["Altuğ Bakan <mail@alt.ug>"]
edition = "2021" edition = "2021"
description = "TFTP Server Daemon implemented in Rust" description = "TFTP Server Daemon implemented in Rust"

View file

@ -3,14 +3,14 @@ use tftpd::{Config, Server};
fn main() { fn main() {
let config = Config::new(env::args()).unwrap_or_else(|err| { let config = Config::new(env::args()).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err); eprintln!("Problem parsing arguments: {err}");
process::exit(1) process::exit(1)
}); });
let server = Server::new(&config).unwrap_or_else(|err| { let server = Server::new(&config).unwrap_or_else(|err| {
eprintln!( eprintln!(
"Problem creating server on {}:{}: {}", "Problem creating server on {}:{}: {err}",
config.ip_address, config.port, err config.ip_address, config.port
); );
process::exit(1) process::exit(1)
}); });

View file

@ -7,17 +7,28 @@ use crate::packet::{ErrorCode, Opcode, Packet, TransferOption};
pub struct Message; pub struct Message;
const MAX_REQUEST_PACKET_SIZE: usize = 512;
impl Message { impl Message {
pub fn send_data(socket: &UdpSocket, data: &[u8]) -> Result<(), Box<dyn Error>> { pub fn send_data(
let buf = [&Opcode::Data.as_bytes()[..], data].concat(); socket: &UdpSocket,
block_number: u16,
data: &[u8],
) -> Result<(), Box<dyn Error>> {
let buf = [
&Opcode::Data.as_bytes()[..],
&block_number.to_be_bytes(),
data,
]
.concat();
socket.send(&buf)?; socket.send(&buf)?;
Ok(()) Ok(())
} }
pub fn send_ack(socket: &UdpSocket, block: u16) -> Result<(), Box<dyn Error>> { pub fn send_ack(socket: &UdpSocket, block_number: u16) -> Result<(), Box<dyn Error>> {
let buf = [Opcode::Ack.as_bytes(), block.to_be_bytes()].concat(); let buf = [Opcode::Ack.as_bytes(), block_number.to_be_bytes()].concat();
socket.send(&buf)?; socket.send(&buf)?;
@ -29,16 +40,21 @@ impl Message {
code: ErrorCode, code: ErrorCode,
msg: &str, msg: &str,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
socket.send(&get_error_buf(code, msg))?; socket.send(&build_error_buf(code, msg))?;
Ok(()) Ok(())
} }
pub fn send_error_to(socket: &UdpSocket, to: &SocketAddr, code: ErrorCode, msg: &str) { pub fn send_error_to<'a>(
eprintln!("{msg}"); socket: &UdpSocket,
if socket.send_to(&get_error_buf(code, msg), to).is_err() { to: &SocketAddr,
code: ErrorCode,
msg: &'a str,
) -> Result<(), Box<dyn Error>> {
if socket.send_to(&build_error_buf(code, msg), to).is_err() {
eprintln!("could not send an error message"); eprintln!("could not send an error message");
} }
Err(msg.into())
} }
pub fn send_oack( pub fn send_oack(
@ -56,19 +72,32 @@ impl Message {
Ok(()) Ok(())
} }
pub fn receive_ack(socket: &UdpSocket) -> Result<u16, Box<dyn Error>> { pub fn recv(socket: &UdpSocket) -> Result<Packet, Box<dyn Error>> {
let mut buf = [0; 4]; let mut buf = [0; MAX_REQUEST_PACKET_SIZE];
socket.recv(&mut buf)?; let number_of_bytes = socket.recv(&mut buf)?;
let packet = Packet::deserialize(&buf[..number_of_bytes])?;
if let Ok(Packet::Ack(block)) = Packet::deserialize(&buf) { Ok(packet)
Ok(block) }
} else {
Err("invalid ack".into()) pub fn recv_data(socket: &UdpSocket, size: usize) -> Result<Packet, Box<dyn Error>> {
} let mut buf = vec![0; size + 4];
let number_of_bytes = socket.recv(&mut buf)?;
let packet = Packet::deserialize(&buf[..number_of_bytes])?;
Ok(packet)
}
pub fn recv_from(socket: &UdpSocket) -> Result<(Packet, SocketAddr), Box<dyn Error>> {
let mut buf = [0; MAX_REQUEST_PACKET_SIZE];
let (number_of_bytes, from) = socket.recv_from(&mut buf)?;
let packet = Packet::deserialize(&buf[..number_of_bytes])?;
Ok((packet, from))
} }
} }
fn get_error_buf(code: ErrorCode, msg: &str) -> Vec<u8> { fn build_error_buf(code: ErrorCode, msg: &str) -> Vec<u8> {
[ [
&Opcode::Error.as_bytes()[..], &Opcode::Error.as_bytes()[..],
&code.as_bytes()[..], &code.as_bytes()[..],

View file

@ -1,7 +1,7 @@
use crate::Convert; use crate::Convert;
use std::error::Error; use std::{error::Error, fmt};
pub enum Packet<'a> { pub enum Packet {
Rrq { Rrq {
filename: String, filename: String,
mode: String, mode: String,
@ -14,7 +14,7 @@ pub enum Packet<'a> {
}, },
Data { Data {
block_num: u16, block_num: u16,
data: &'a [u8], data: Vec<u8>,
}, },
Ack(u16), Ack(u16),
Error { Error {
@ -23,9 +23,9 @@ pub enum Packet<'a> {
}, },
} }
impl<'a> Packet<'a> { impl Packet {
pub fn deserialize(buf: &'a [u8]) -> Result<Packet, Box<dyn Error>> { pub fn deserialize(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
let opcode = Opcode::from_u16(Convert::to_u16(&buf[0..1])?)?; let opcode = Opcode::from_u16(Convert::to_u16(&buf[0..=1])?)?;
match opcode { match opcode {
Opcode::Rrq | Opcode::Wrq => parse_rq(buf, opcode), Opcode::Rrq | Opcode::Wrq => parse_rq(buf, opcode),
@ -66,7 +66,7 @@ impl Opcode {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct TransferOption { pub struct TransferOption {
pub option: OptionType, pub option: OptionType,
pub value: usize, pub value: usize,
@ -84,7 +84,7 @@ impl TransferOption {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum OptionType { pub enum OptionType {
BlockSize, BlockSize,
TransferSize, TransferSize,
@ -100,10 +100,6 @@ impl OptionType {
} }
} }
fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
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),
@ -147,6 +143,21 @@ impl ErrorCode {
} }
} }
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorCode::NotDefined => write!(f, "Not Defined"),
ErrorCode::FileNotFound => write!(f, "File Not Found"),
ErrorCode::AccessViolation => write!(f, "Access Violation"),
ErrorCode::DiskFull => write!(f, "Disk Full"),
ErrorCode::IllegalOperation => write!(f, "Illegal Operation"),
ErrorCode::UnknownId => write!(f, "Unknown ID"),
ErrorCode::FileExists => write!(f, "File Exists"),
ErrorCode::NoSuchUser => write!(f, "No Such User"),
}
}
}
fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> { fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> {
let mut options = vec![]; let mut options = vec![];
let filename: String; let filename: String;
@ -188,7 +199,7 @@ fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> {
fn parse_data(buf: &[u8]) -> Result<Packet, Box<dyn Error>> { fn parse_data(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
Ok(Packet::Data { Ok(Packet::Data {
block_num: Convert::to_u16(&buf[2..])?, block_num: Convert::to_u16(&buf[2..])?,
data: &buf[4..], data: buf[4..].to_vec(),
}) })
} }
@ -239,11 +250,11 @@ mod tests {
&[0x00], &[0x00],
&"octet".as_bytes(), &"octet".as_bytes(),
&[0x00], &[0x00],
&OptionType::TransferSize.as_bytes(), &OptionType::TransferSize.as_str().as_bytes(),
&[0x00], &[0x00],
&"0".as_bytes(), &"0".as_bytes(),
&[0x00], &[0x00],
&OptionType::Timeout.as_bytes(), &OptionType::Timeout.as_str().as_bytes(),
&[0x00], &[0x00],
&"5".as_bytes(), &"5".as_bytes(),
&[0x00], &[0x00],
@ -311,11 +322,11 @@ mod tests {
&[0x00], &[0x00],
&"octet".as_bytes(), &"octet".as_bytes(),
&[0x00], &[0x00],
&OptionType::TransferSize.as_bytes(), &OptionType::TransferSize.as_str().as_bytes(),
&[0x00], &[0x00],
&"12341234".as_bytes(), &"12341234".as_bytes(),
&[0x00], &[0x00],
&OptionType::BlockSize.as_bytes(), &OptionType::BlockSize.as_str().as_bytes(),
&[0x00], &[0x00],
&"1024".as_bytes(), &"1024".as_bytes(),
&[0x00], &[0x00],

View file

@ -2,9 +2,7 @@ use crate::packet::{ErrorCode, Packet, TransferOption};
use crate::{Config, Message, Worker}; use crate::{Config, Message, Worker};
use std::error::Error; use std::error::Error;
use std::net::{SocketAddr, UdpSocket}; use std::net::{SocketAddr, UdpSocket};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
const MAX_REQUEST_PACKET_SIZE: usize = 512;
pub struct Server { pub struct Server {
socket: UdpSocket, socket: UdpSocket,
@ -25,63 +23,86 @@ impl Server {
pub fn listen(&self) { pub fn listen(&self) {
loop { loop {
let mut buf = [0; MAX_REQUEST_PACKET_SIZE]; if let Ok((packet, from)) = Message::recv_from(&self.socket) {
if let Ok((number_of_bytes, from)) = self.socket.recv_from(&mut buf) { match packet {
if let Ok(packet) = Packet::deserialize(&buf[..number_of_bytes]) { Packet::Rrq {
self.handle_packet(&packet, &from) filename,
} mut options,
..
} => match self.handle_rrq(filename.clone(), &mut options, &from) {
Ok(_) => {
println!("Sending {filename} to {from}");
}
Err(err) => eprintln!("{err}"),
},
Packet::Wrq {
filename,
mut options,
..
} => match self.handle_wrq(filename.clone(), &mut options, &from) {
Ok(_) => {
println!("Receiving {filename} from {from}");
}
Err(err) => eprintln!("{err}"),
},
_ => {
Message::send_error_to(
&self.socket,
&from,
ErrorCode::IllegalOperation,
"invalid request",
)
.unwrap_or_else(|err| eprintln!("{err}"));
}
};
} }
} }
} }
fn handle_packet(&self, packet: &Packet, from: &SocketAddr) { fn handle_rrq(
match &packet { &self,
Packet::Rrq { filename: String,
filename, options, .. options: &mut Vec<TransferOption>,
} => self.validate_rrq(filename, options, from), to: &SocketAddr,
Packet::Wrq { ) -> Result<(), Box<dyn Error>> {
filename, options, .. match check_file_exists(&get_full_path(&filename, &self.directory), &self.directory) {
} => self.validate_wrq(filename, options, from),
_ => {
Message::send_error_to(
&self.socket,
from,
ErrorCode::IllegalOperation,
"invalid request",
);
}
}
}
fn validate_rrq(&self, filename: &String, options: &Vec<TransferOption>, to: &SocketAddr) {
match self.check_file_exists(&Path::new(&filename)) {
ErrorCode::FileNotFound => { ErrorCode::FileNotFound => {
Message::send_error_to( return Message::send_error_to(
&self.socket, &self.socket,
to, to,
ErrorCode::FileNotFound, ErrorCode::FileNotFound,
"requested file does not exist", "file does not exist",
); );
} }
ErrorCode::AccessViolation => { ErrorCode::AccessViolation => {
Message::send_error_to( return Message::send_error_to(
&self.socket, &self.socket,
to, to,
ErrorCode::AccessViolation, ErrorCode::AccessViolation,
"requested file is not in the directory", "file access violation",
); );
} }
ErrorCode::FileExists => self ErrorCode::FileExists => Worker::send(
.handle_rrq(filename, options, to) self.socket.local_addr().unwrap(),
.unwrap_or_else(|err| eprintln!("could not handle read request: {err}")), *to,
filename,
options.to_vec(),
),
_ => {} _ => {}
} }
Ok(())
} }
fn validate_wrq(&self, filename: &String, options: &Vec<TransferOption>, to: &SocketAddr) { fn handle_wrq(
match self.check_file_exists(&Path::new(&filename)) { &self,
filename: String,
options: &mut Vec<TransferOption>,
to: &SocketAddr,
) -> Result<(), Box<dyn Error>> {
match check_file_exists(&get_full_path(&filename, &self.directory), &self.directory) {
ErrorCode::FileExists => { ErrorCode::FileExists => {
Message::send_error_to( return Message::send_error_to(
&self.socket, &self.socket,
to, to,
ErrorCode::FileExists, ErrorCode::FileExists,
@ -89,53 +110,85 @@ impl Server {
); );
} }
ErrorCode::AccessViolation => { ErrorCode::AccessViolation => {
Message::send_error_to( return Message::send_error_to(
&self.socket, &self.socket,
to, to,
ErrorCode::AccessViolation, ErrorCode::AccessViolation,
"requested file is not in the directory", "file access violation",
); );
} }
ErrorCode::FileNotFound => self ErrorCode::FileNotFound => Worker::receive(
.handle_wrq(filename, options, to) self.socket.local_addr().unwrap(),
.unwrap_or_else(|err| eprintln!("could not handle write request: {err}")), *to,
filename,
options.to_vec(),
),
_ => {} _ => {}
}; };
}
fn handle_rrq(
&self,
filename: &String,
options: &Vec<TransferOption>,
to: &SocketAddr,
) -> Result<(), Box<dyn Error>> {
let mut worker = Worker::new(&self.socket.local_addr().unwrap(), to)?;
worker.send_file(Path::new(&filename), options)?;
Ok(()) Ok(())
} }
}
fn handle_wrq(
&self, fn check_file_exists(file: &PathBuf, directory: &PathBuf) -> ErrorCode {
filename: &String, if !validate_file_path(file, directory) {
options: &Vec<TransferOption>, return ErrorCode::AccessViolation;
to: &SocketAddr, }
) -> Result<(), Box<dyn Error>> {
let mut worker = Worker::new(&self.socket.local_addr().unwrap(), to)?; if !file.exists() {
worker.receive_file(Path::new(&filename), options)?; return ErrorCode::FileNotFound;
}
Ok(())
} ErrorCode::FileExists
}
fn check_file_exists(&self, file: &Path) -> ErrorCode {
if !file.ancestors().any(|a| a == &self.directory) { fn validate_file_path(file: &PathBuf, directory: &PathBuf) -> bool {
return ErrorCode::AccessViolation; !file.to_str().unwrap().contains("..") && file.ancestors().any(|a| a == directory)
} }
if !file.exists() { fn get_full_path(filename: &str, directory: &PathBuf) -> PathBuf {
return ErrorCode::FileNotFound; let mut file = directory.clone();
} file.push(PathBuf::from(filename));
file
ErrorCode::FileExists }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gets_full_path() {
assert_eq!(
get_full_path("test.txt", &PathBuf::from("/dir/test")),
PathBuf::from("/dir/test/test.txt")
);
assert_eq!(
get_full_path("some_dir/test.txt", &PathBuf::from("/dir/test")),
PathBuf::from("/dir/test/some_dir/test.txt")
);
}
#[test]
fn validates_file_path() {
assert!(validate_file_path(
&PathBuf::from("/dir/test/file"),
&PathBuf::from("/dir/test")
));
assert!(!validate_file_path(
&PathBuf::from("/system/data.txt"),
&PathBuf::from("/dir/test")
));
assert!(!validate_file_path(
&PathBuf::from("~/some_data.txt"),
&PathBuf::from("/dir/test")
));
assert!(!validate_file_path(
&PathBuf::from("/dir/test/../file"),
&PathBuf::from("/dir/test")
));
}
} }

View file

@ -1,111 +1,242 @@
use std::{ use std::{
error::Error, error::Error,
fs::File, fs::File,
io::Read, io::{Read, Write},
net::{SocketAddr, UdpSocket}, net::{SocketAddr, UdpSocket},
path::Path, path::Path,
thread,
time::Duration, time::Duration,
}; };
use crate::{ use crate::{
packet::{OptionType, TransferOption}, packet::{ErrorCode, OptionType, Packet, TransferOption},
Message, Message,
}; };
pub struct Worker { pub struct Worker;
socket: UdpSocket,
pub struct WorkerOptions {
blk_size: usize, blk_size: usize,
t_size: usize, t_size: usize,
timeout: usize, timeout: u64,
}
#[derive(PartialEq, Eq)]
enum WorkType {
Receive,
Send(u64),
} }
const MAX_RETRIES: u32 = 6; const MAX_RETRIES: u32 = 6;
const DEFAULT_TIMEOUT_SECS: u64 = 5;
const DEFAULT_BLOCK_SIZE: usize = 512;
impl Worker { impl Worker {
pub fn new(addr: &SocketAddr, remote: &SocketAddr) -> Result<Worker, Box<dyn Error>> { pub fn send(
let socket = UdpSocket::bind(SocketAddr::from((addr.ip(), 0)))?; addr: SocketAddr,
socket.connect(remote)?; remote: SocketAddr,
Ok(Worker { filename: String,
socket, mut options: Vec<TransferOption>,
blk_size: 512, ) {
t_size: 0, thread::spawn(move || {
timeout: 5, let mut handle_send = || -> Result<(), Box<dyn Error>> {
}) let socket = setup_socket(&addr, &remote)?;
let work_type = WorkType::Send(Path::new(&filename).metadata().unwrap().len());
let worker_options = parse_options(&mut options, &work_type)?;
send_oack(&socket, &options, &work_type)?;
send_file(&socket, &worker_options, &filename, &mut options)?;
Ok(())
};
if let Err(err) = handle_send() {
eprintln!("{err}");
}
});
} }
pub fn send_file( pub fn receive(
&mut self, addr: SocketAddr,
file: &Path, remote: SocketAddr,
options: &Vec<TransferOption>, filename: String,
) -> Result<(), Box<dyn Error>> { mut options: Vec<TransferOption>,
let mut file = File::open(file).unwrap(); ) {
thread::spawn(move || {
let mut handle_receive = || -> Result<(), Box<dyn Error>> {
let socket = setup_socket(&addr, &remote)?;
let work_type = WorkType::Receive;
let worker_options = parse_options(&mut options, &work_type)?;
send_oack(&socket, &options, &work_type)?;
receive_file(&socket, &worker_options, &filename, &mut options)?;
self.parse_options(options, Some(&file)); Ok(())
Message::send_oack(&self.socket, options)?; };
self.socket if let Err(err) = handle_receive() {
.set_write_timeout(Some(Duration::from_secs(self.timeout as u64)))?; eprintln!("{err}");
}
});
}
}
fn send_file(
socket: &UdpSocket,
worker_options: &WorkerOptions,
filename: &String,
options: &mut Vec<TransferOption>,
) -> Result<(), Box<dyn Error>> {
let mut file = File::open(filename).unwrap();
parse_options(options, &WorkType::Send(file.metadata().unwrap().len()))?;
let mut block_number = 1;
loop {
let mut chunk = vec![0; worker_options.blk_size];
let size = file.read(&mut chunk)?;
let mut retry_cnt = 0; let mut retry_cnt = 0;
loop { loop {
let mut chunk = Vec::with_capacity(self.blk_size); Message::send_data(socket, block_number, &chunk[..size])?;
let size = file
.by_ref()
.take(self.blk_size as u64)
.read_to_end(&mut chunk)?;
loop { match Message::recv(socket) {
if Message::send_data(&self.socket, &chunk).is_err() { Ok(Packet::Ack(received_block_number)) => {
return Err(format!("failed to send data").into()); if received_block_number == block_number {
block_number = block_number.wrapping_add(1);
break;
}
} }
Ok(Packet::Error { code, msg }) => {
if let Ok(block) = Message::receive_ack(&self.socket) { return Err(format!("received error code {code}, with message {msg}").into());
todo!("handle block number"); }
} else { _ => {
retry_cnt += 1; retry_cnt += 1;
if retry_cnt == MAX_RETRIES { if retry_cnt == MAX_RETRIES {
return Err(format!("transfer timed out after {MAX_RETRIES} tries").into()); return Err(format!("transfer timed out after {MAX_RETRIES} tries").into());
} }
} }
} }
if size < self.blk_size {
break;
};
} }
Ok(()) if size < worker_options.blk_size {
break;
};
} }
pub fn receive_file( println!("Sent {filename} to {}", socket.peer_addr().unwrap());
&mut self, Ok(())
file: &Path, }
options: &Vec<TransferOption>,
) -> Result<(), Box<dyn Error>> {
let mut file = File::open(file).unwrap();
self.parse_options(options, Some(&file)); fn receive_file(
Message::send_oack(&self.socket, options)?; socket: &UdpSocket,
worker_options: &WorkerOptions,
filename: &String,
options: &mut Vec<TransferOption>,
) -> Result<(), Box<dyn Error>> {
let mut file = File::create(filename).unwrap();
todo!("file receiving"); parse_options(options, &WorkType::Receive)?;
Ok(()) let mut block_number: u16 = 0;
} loop {
let size;
fn parse_options(&mut self, options: &Vec<TransferOption>, file: Option<&File>) { let mut retry_cnt = 0;
for option in options { loop {
let TransferOption { option, value } = option; match Message::recv_data(socket, worker_options.blk_size) {
Ok(Packet::Data {
match option { block_num: received_block_number,
OptionType::BlockSize => self.blk_size = *value, data,
OptionType::TransferSize => { }) => {
self.t_size = match file { if received_block_number == block_number.wrapping_add(1) {
Some(file) => file.metadata().unwrap().len() as usize, block_number = received_block_number;
None => *value, file.write(&data)?;
size = data.len();
break;
} }
} }
OptionType::Timeout => self.timeout = *value, Ok(Packet::Error { code, msg }) => {
return Err(format!("received error code {code}: {msg}").into());
}
Err(err) => {
retry_cnt += 1;
if retry_cnt == MAX_RETRIES {
return Err(
format!("transfer timed out after {MAX_RETRIES} tries: {err}").into(),
);
}
}
_ => {}
}
}
Message::send_ack(socket, block_number)?;
if size < worker_options.blk_size {
break;
};
}
println!("Received {filename} from {}", socket.peer_addr().unwrap());
Ok(())
}
fn setup_socket(addr: &SocketAddr, remote: &SocketAddr) -> Result<UdpSocket, Box<dyn Error>> {
let socket = UdpSocket::bind(SocketAddr::from((addr.ip(), 0)))?;
socket.connect(remote)?;
socket.set_read_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
socket.set_write_timeout(Some(Duration::from_secs(DEFAULT_TIMEOUT_SECS)))?;
Ok(socket)
}
fn parse_options(
options: &mut Vec<TransferOption>,
work_type: &WorkType,
) -> Result<WorkerOptions, Box<dyn Error>> {
let mut worker_options = WorkerOptions {
blk_size: DEFAULT_BLOCK_SIZE,
t_size: 0,
timeout: DEFAULT_TIMEOUT_SECS,
};
for option in &mut *options {
let TransferOption { option, value } = option;
match option {
OptionType::BlockSize => worker_options.blk_size = *value,
OptionType::TransferSize => match work_type {
WorkType::Send(size) => {
*value = *size as usize;
}
WorkType::Receive => {
worker_options.t_size = *value;
}
},
OptionType::Timeout => {
if *value == 0 {
return Err("invalid timeout value".into());
}
worker_options.timeout = *value as u64;
} }
} }
} }
Ok(worker_options)
}
fn send_oack(
socket: &UdpSocket,
options: &Vec<TransferOption>,
work_type: &WorkType,
) -> Result<(), Box<dyn Error>> {
if options.len() > 0 {
Message::send_oack(socket, options)?;
if let Packet::Ack(received_block_number) = Message::recv(socket)? {
if received_block_number != 0 {
Message::send_error(socket, ErrorCode::IllegalOperation, "invalid oack response")?;
}
}
} else if *work_type == WorkType::Receive {
Message::send_ack(socket, 0)?
}
Ok(())
} }