Add overall work
This commit is contained in:
parent
2eb4329201
commit
3ba7352692
6 changed files with 270 additions and 35 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
65
src/message.rs
Normal 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()
|
||||
}
|
||||
|
|
@ -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 })
|
||||
}
|
||||
|
|
|
|||
138
src/server.rs
138
src/server.rs
|
|
@ -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
27
src/worker.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue