Add overall work

This commit is contained in:
altugbakan 2023-03-07 20:12:42 +03:00
parent 2eb4329201
commit 3ba7352692
6 changed files with 270 additions and 35 deletions

View file

@ -11,12 +11,61 @@ impl Convert {
}
}
pub fn to_string(buf: &[u8]) -> Result<(String, usize), Box<dyn Error>> {
let zero_index = match buf[2..].iter().position(|&b| b == 0x00) {
Some(index) => index,
pub fn to_string(buf: &[u8], start: usize) -> Result<(String, usize), Box<dyn Error>> {
match buf[start..].iter().position(|&b| b == 0x00) {
Some(index) => Ok((
String::from_utf8(buf[start..start + index].to_vec())?,
index + start,
)),
None => return Err("invalid string".into()),
};
Ok((String::from_utf8(buf[2..zero_index].to_vec())?, zero_index))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn converts_to_u16() {
assert_eq!(Convert::to_u16(&[0x01, 0x02]).unwrap(), 0x0102);
assert_eq!(Convert::to_u16(&[0x00, 0x02]).unwrap(), 0x0002);
assert_eq!(Convert::to_u16(&[0xfe, 0xdc, 0xba]).unwrap(), 0xfedc);
}
#[test]
fn returns_error_on_short_array() {
assert!(Convert::to_u16(&[0x01]).is_err());
assert!(Convert::to_u16(&[]).is_err());
}
#[test]
fn converts_to_string() {
let (result, index) = Convert::to_string(b"hello world\0", 0).unwrap();
assert_eq!(result, "hello world");
assert_eq!(index, 11);
let (result, index) = Convert::to_string(b"hello\0world", 0).unwrap();
assert_eq!(result, "hello");
assert_eq!(index, 5);
let (result, index) = Convert::to_string(b"\0hello world", 0).unwrap();
assert_eq!(result, "");
assert_eq!(index, 0);
}
#[test]
fn converts_to_string_with_index() {
let (result, index) = Convert::to_string(b"hello\0world\0", 0).unwrap();
assert_eq!(result, "hello");
assert_eq!(index, 5);
let (result, index) = Convert::to_string(b"hello\0world\0", 5).unwrap();
assert_eq!(result, "");
assert_eq!(index, 5);
let (result, index) = Convert::to_string(b"hello\0world\0", 6).unwrap();
assert_eq!(result, "world");
assert_eq!(index, 11);
}
}

View file

@ -1,8 +1,12 @@
mod config;
mod convert;
mod message;
mod packet;
mod server;
mod worker;
pub use config::Config;
pub use convert::Convert;
pub use message::Message;
pub use server::Server;
pub use worker::Worker;

65
src/message.rs Normal file
View file

@ -0,0 +1,65 @@
use std::{
error::Error,
net::{SocketAddr, UdpSocket},
};
use crate::packet::{ErrorCode, Opcode, Option};
pub struct Message;
impl Message {
pub fn send_data(socket: &UdpSocket, data: &[u8]) -> Result<(), Box<dyn Error>> {
let buf = [&[0x00, Opcode::Data as u8], data].concat();
socket.send(&buf)?;
Ok(())
}
pub fn send_ack(socket: &UdpSocket, block: u16) -> Result<(), Box<dyn Error>> {
let buf = [&[0x00, Opcode::Ack as u8], &block.to_be_bytes()[..]].concat();
socket.send(&buf)?;
Ok(())
}
pub fn send_error(
socket: &UdpSocket,
code: ErrorCode,
msg: &str,
) -> Result<(), Box<dyn Error>> {
socket.send(&get_error_buf(code, msg))?;
Ok(())
}
pub fn send_error_to(socket: &UdpSocket, to: &SocketAddr, code: ErrorCode, msg: &str) {
if socket.send_to(&get_error_buf(code, msg), to).is_err() {
eprintln!("could not send an error message");
}
}
pub fn send_oack_to(
socket: &UdpSocket,
to: &SocketAddr,
options: Vec<Option>,
) -> Result<(), Box<dyn Error>> {
todo!();
let buf = [];
socket.send_to(&buf, to)?;
Ok(())
}
}
fn get_error_buf(code: ErrorCode, msg: &str) -> Vec<u8> {
[
&[0x00, Opcode::Error as u8, 0x00, code as u8],
msg.as_bytes(),
&[0x00],
]
.concat()
}

View file

@ -97,14 +97,14 @@ fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> {
let mode: String;
let mut zero_index: usize;
(filename, zero_index) = Convert::to_string(&buf[2..])?;
(mode, zero_index) = Convert::to_string(&buf[zero_index + 1..])?;
(filename, zero_index) = Convert::to_string(buf, 2)?;
(mode, zero_index) = Convert::to_string(buf, zero_index + 1)?;
let mut value: String;
let mut option;
while zero_index < buf.len() - 1 {
(option, zero_index) = Convert::to_string(&buf[zero_index + 1..])?;
(value, zero_index) = Convert::to_string(&buf[zero_index + 1..])?;
(option, zero_index) = Convert::to_string(buf, zero_index + 1)?;
(value, zero_index) = Convert::to_string(buf, zero_index + 1)?;
options.push(Option { option, value });
}
@ -136,6 +136,6 @@ fn parse_ack(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
fn parse_error(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
let code = ErrorCode::from_u16(Convert::to_u16(&buf[2..])?)?;
let (msg, _) = Convert::to_string(&buf[4..])?;
let (msg, _) = Convert::to_string(buf, 4)?;
Ok(Packet::Error { code, msg })
}

View file

@ -1,19 +1,24 @@
use crate::packet::{ErrorCode, Opcode, Packet};
use crate::Config;
use crate::packet::{ErrorCode, Packet};
use crate::{Config, Message, Worker};
use std::error::Error;
use std::net::{SocketAddr, UdpSocket};
use std::path::{Path, PathBuf};
const MAX_REQUEST_PACKET_SIZE: usize = 512;
pub struct Server {
socket: UdpSocket,
directory: PathBuf,
}
impl Server {
pub fn new(config: &Config) -> Result<Server, Box<dyn Error>> {
let socket = UdpSocket::bind(SocketAddr::from((config.ip_address, config.port)))?;
let server = Server { socket };
let server = Server {
socket,
directory: config.directory.clone(),
};
Ok(server)
}
@ -23,32 +28,117 @@ impl Server {
let mut buf = [0; MAX_REQUEST_PACKET_SIZE];
if let Ok((number_of_bytes, from)) = self.socket.recv_from(&mut buf) {
if let Ok(packet) = Packet::deserialize(&buf[..number_of_bytes]) {
match packet {
Packet::Rrq {
filename,
mode,
options,
} => todo!(),
Packet::Wrq {
filename,
mode,
options,
} => todo!(),
_ => self.send_error_msg(from),
match &packet {
Packet::Rrq { filename, .. } => {
match self.check_file_exists(&Path::new(&filename)) {
ErrorCode::FileNotFound => {
eprintln!("requested file does not exist");
Message::send_error_to(
&self.socket,
&from,
ErrorCode::FileNotFound,
"requested file does not exist",
);
}
ErrorCode::AccessViolation => {
eprintln!("requested file is not in the directory");
Message::send_error_to(
&self.socket,
&from,
ErrorCode::AccessViolation,
"requested file is not in the directory",
);
}
ErrorCode::FileExists => self
.handle_rrq(&packet, from)
.unwrap_or_else(|_| eprintln!("could not handle read request")),
_ => {}
}
}
Packet::Wrq { filename, .. } => {
match self.check_file_exists(&Path::new(&filename)) {
ErrorCode::FileExists => {
eprintln!("requested file already exists");
Message::send_error_to(
&self.socket,
&from,
ErrorCode::FileExists,
"requested file already exists",
);
}
ErrorCode::AccessViolation => {
eprintln!("requested file is not in the directory");
Message::send_error_to(
&self.socket,
&from,
ErrorCode::AccessViolation,
"requested file is not in the directory",
);
}
ErrorCode::FileNotFound => {
self.handle_wrq(&packet, from).unwrap_or_else(|_| {
eprintln!("could not handle write request")
})
}
_ => {}
}
}
_ => {
eprintln!("invalid request packet received");
Message::send_error_to(
&self.socket,
&from,
ErrorCode::IllegalOperation,
"invalid request",
);
}
}
};
}
}
}
fn send_error_msg(&self, to: SocketAddr) {
let buf = [
0x00,
Opcode::Error as u8,
0x00,
ErrorCode::IllegalOperation as u8,
0x00,
];
self.socket.send_to(&buf, to);
fn handle_rrq(&self, packet: &Packet, to: SocketAddr) -> Result<(), Box<dyn Error>> {
if let Packet::Rrq {
filename,
mode: _,
options,
} = packet
{
let worker = Worker::new(self.socket.local_addr().unwrap(), to)?;
worker.send_file(Path::new(&filename), options)?;
} else {
return Err("invalid read request packet".into());
}
Ok(())
}
fn handle_wrq(&self, packet: &Packet, to: SocketAddr) -> Result<(), Box<dyn Error>> {
if let Packet::Wrq {
filename,
mode: _,
options,
} = packet
{
let worker = Worker::new(self.socket.local_addr().unwrap(), to)?;
worker.receive_file(Path::new(&filename), options)?;
} else {
return Err("invalid write request packet".into());
}
Ok(())
}
fn check_file_exists(&self, file: &Path) -> ErrorCode {
if !file.ancestors().any(|a| a == &self.directory) {
return ErrorCode::AccessViolation;
}
if !file.exists() {
return ErrorCode::FileNotFound;
}
ErrorCode::FileExists
}
}

27
src/worker.rs Normal file
View file

@ -0,0 +1,27 @@
use std::{
error::Error,
net::{SocketAddr, UdpSocket},
path::Path,
};
use crate::packet::Option;
pub struct Worker {
socket: UdpSocket,
}
impl Worker {
pub fn new(addr: SocketAddr, remote: SocketAddr) -> Result<Worker, Box<dyn Error>> {
let socket = UdpSocket::bind(SocketAddr::from((addr.ip(), 0)))?;
socket.connect(remote)?;
Ok(Worker { socket })
}
pub fn send_file(&self, file: &Path, options: &Vec<Option>) -> Result<(), Box<dyn Error>> {
Ok(())
}
pub fn receive_file(&self, file: &Path, options: &Vec<Option>) -> Result<(), Box<dyn Error>> {
Ok(())
}
}