From ea699988a05e3d19f6fbd070717e88ccf0d2466e Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 15:46:20 +0200 Subject: [PATCH 01/11] Clean up code and make it more robust --- src/main.rs | 222 ++++++++++++++++++++++++++-------------------------- 1 file changed, 109 insertions(+), 113 deletions(-) diff --git a/src/main.rs b/src/main.rs index 25e84ca..60c48d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,118 +13,124 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - extern crate byteorder; -use std::io::Cursor; + +use byteorder::ReadBytesExt; +use byteorder::{BigEndian, WriteBytesExt}; + +use std::collections::HashMap; +use std::env; use std::fs::File; use std::io::BufReader; +use std::io::Cursor; use std::io::prelude::*; -use std::net; -use std::collections::HashMap; use std::io::SeekFrom; -use byteorder::ReadBytesExt; -use byteorder::{BigEndian, WriteBytesExt}; -use std::error::Error; +use std::net; use std::str; -struct Connection<'a>{ - src: &'a net::SocketAddr, +struct Connection<'a> { + src: &'a net::SocketAddr, socket: &'a net::UdpSocket, } - -impl<'a> Connection<'a>{ - pub fn send_response(&self, data: Vec) { - let result = self.socket.send_to(&data, &self.src); +impl<'a> Connection<'a> { + pub fn send_response(&self, data: &[u8]) { + let result = self.socket.send_to(data, &self.src); match result { - Ok(amt) => println!("Sent response with {} bytes", amt), - Err(err) => panic!("Write error: {}", err) + Ok(_) => {} + Err(err) => panic!("Write error: {}", err), } } + pub fn send_error(&self, code: u16, err: &str) { + let mut message = Vec::new(); + message.push((code >> 8) as u8); + message.push(code as u8); + message.extend(err.as_bytes()); + self.send_response(&message); + } } -struct FileStream{ +struct FileStream { reader: BufReader, + chunks: u64, + pos: u64, + done: bool, } +impl FileStream { + pub fn new(data: &mut Cursor<&Vec>, amt: &usize) -> FileStream { + let mut parts = data.get_ref().as_slice()[2..].split(|b| *b == b'\x00'); + let name = str::from_utf8(parts.next().unwrap()).unwrap(); + let mode = str::from_utf8(parts.next().unwrap()).unwrap(); + println!("Request name: {:?}", name); + println!("Mode: {:?}", mode); -impl FileStream{ - pub fn new(data: &mut Cursor<&Vec>, amt: &usize,) -> FileStream { - - let mut index = 2; - for x in 2..20 { - if data.get_ref().as_slice()[x] == 0{ - index = x; - break; - } - } - let mut full_path = String::from("/home/ayoung/tftp/"); - let filename = match str::from_utf8(&data.get_ref().as_slice()[2..index]) { - Ok(file_name) => file_name, - Err(why) => panic!("couldn't read filename: {}", - Error::description(&why)), - }; - full_path.push_str(filename); - println!("filename: {}", filename); - - index += 1; - - let mode = match str::from_utf8(&data.get_ref().as_slice()[index..*amt]) { - Ok(v) => v, - Err(e) => panic!("Invalid UTF-8 sequence: {}", e), - }; - println!("mode: {}", mode); - println!("amount: {}", amt); - println!("mode length: {}", mode.len()); - + let mut abs_path = env::current_dir().unwrap(); + abs_path.push(name); + println!("Absolute path: {:?}", abs_path.display()); - let file = match File::open(full_path){ + let file = match File::open(abs_path) { Err(err) => panic!("Can't open file: {}", err), Ok(file) => file, }; + let len = file.metadata().unwrap().len(); + println!("Found file length: {}", len); let reader = BufReader::new(file); - return FileStream{ + let chunks = if len % 512 == 0 { + len / 512 + } else { + len / 512 + 1 + }; + println!("Calculated length of {} chunks", chunks); + return FileStream { reader: reader, + chunks, + pos: 0, + done: false, }; } - pub fn send_chunk(&mut self, chunk: &u16, connection: &Connection){ - println!("Sending chunk {}" , chunk); - - let chunk2: u64 = *chunk as u64; - let offset: u64 = ((chunk2 - 1) * 512) as u64; - match self.reader.seek(SeekFrom::Start(offset)) { - Ok(amt) => println!("Sent {} bytes", amt), - Err(err) => panic!("Write error: {}", err) + pub fn send_chunk(&mut self, chunk: u64, connection: &Connection) { + if chunk > self.chunks { + let end = [0u8, 3, (chunk >> 8) as u8, chunk as u8]; + connection.send_response(&end); + println!("Requested chunks past end -> sent empty DATA and set to done"); + self.done = true; + return; } - let mut buf=[0u8; 512]; - let bytes_read; - let result = self.reader.read(&mut buf); - match result { - Ok(l) => bytes_read = l, - Err(e) => { - let mut message:Vec =Vec::new(); - message.push(0); - message.push(5); - message.extend(e.to_string().into_bytes()); - connection.send_response(message); - - return + + let offset = (chunk - 1) * 512; + if offset != self.pos { + println!("Seeking to offset {}", offset); + match self.reader.seek(SeekFrom::Start(offset)) { + Ok(new_pos) => println!("Seek to {}", new_pos), + Err(err) => panic!("Seek error: {}", err), } + } + let mut buf = Vec::with_capacity(516); + buf.write_u16::(3).unwrap(); + buf.write_u16::(chunk as u16).unwrap(); + buf.resize(516, 0); + let read = match self.reader.read(&mut buf[4..]) { + Ok(l) => l, + Err(e) => { + println!("Send read error {}", e); + connection.send_error(0, &e.to_string()); + return; + } + }; + if read < 512 { + println!("Sending incomplete block -> set to done"); + self.done = true; } - let content = buf.to_vec(); - let mut message:Vec =Vec::new(); - message.push(0); - message.push(3); - message.write_u16::(*chunk).unwrap(); - - for i in 0..bytes_read{ - message.push(content[i]); + + self.pos = offset + (read as u64); + connection.send_response(&buf[..4 + read]); + if chunk % 1000 == 0 || chunk > self.chunks - 10 { + println!("Sent block {} with {} bytes", chunk, read); } - connection.send_response(message); - println!("sending block : {}", chunk); } } @@ -135,82 +141,72 @@ fn socket(listen_on: net::SocketAddr) -> net::UdpSocket { Ok(sock) => { println!("Bound socket to {}", listen_on); socket = sock; - }, - Err(err) => panic!("Could not bind: {}", err) + } + Err(err) => panic!("Could not bind: {}", err), } socket } - -fn handle_read_request(data: &mut Cursor<&Vec>, amt: &usize,connection: &Connection) ->FileStream { - - let chunk = 1; - let mut stream = FileStream::new(data, &amt); - stream.send_chunk(&chunk, connection); - - stream -} - - - fn read_message(socket: &net::UdpSocket) { let mut file_streams = HashMap::new(); let mut buf: [u8; 100] = [0; 100]; - loop{ + loop { let result = socket.recv_from(&mut buf); match result { Ok((amt, src)) => { let data = Vec::from(&buf[0..amt]); - let connection = Connection{socket: socket, src: &src}; + let connection = Connection { + socket: socket, + src: &src, + }; let mut rdr = Cursor::new(&data); - if amt < 2{ + if amt < 2 { panic!("Not enough data in packet") } let opcode = rdr.read_u16::().unwrap(); match opcode { 1 => { - file_streams.insert(src, handle_read_request( - &mut rdr, &amt, &connection)); - }, + let mut stream = FileStream::new(&mut rdr, &amt); + stream.send_chunk(1, &connection); + file_streams.insert(src, stream); + } 2 => println!("Write"), 3 => println!("Data"), 4 => { - let chunk = rdr.read_u16::().unwrap() + 1; - file_streams.get_mut(&src).unwrap().send_chunk(&chunk, &connection); - }, + let stream = file_streams.get_mut(&src).unwrap(); + if !stream.done { + let chunk = rdr.read_u16::().unwrap() + 1; + stream.send_chunk(chunk as u64, &connection); + } + } 5 => println!("ERROR"), _ => println!("Illegal Op code"), } - }, - Err(err) => panic!("Read error: {}", err) + } + Err(err) => panic!("Read error: {}", err), } } - } - -pub fn send_response(socket: &net::UdpSocket,src: &net::SocketAddr, data: Vec) { +pub fn send_response(socket: &net::UdpSocket, src: &net::SocketAddr, data: &Vec) { let result = socket.send_to(&data, src); match result { Ok(amt) => println!("Sent {} bytes", amt), - Err(err) => panic!("Write error: {}", err) + Err(err) => panic!("Write error: {}", err), } } - pub fn listen(listen_on: net::SocketAddr) { let socket = socket(listen_on); read_message(&socket) } -fn main(){ - - - let ip = net::Ipv4Addr::new(127, 0, 0, 1); - let listen_addr = net::SocketAddrV4::new(ip, 8888); +fn main() { + let ip = net::Ipv4Addr::new(0, 0, 0, 0); + let listen_addr = net::SocketAddrV4::new(ip, 69); listen(net::SocketAddr::V4(listen_addr)); } From 87a72b883997d6c2445d97043ee3c86cdb11c440 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:12:17 +0200 Subject: [PATCH 02/11] Add Cargo lockfile --- Cargo.lock | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5aa2a96 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,14 @@ +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tftpd" +version = "0.0.1" +dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" From c8cc646a0793d68145cfb460d036704028aedbee Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:13:01 +0200 Subject: [PATCH 03/11] Add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ From cefe64aaa18d8c5d183c984a8dec80de2f599dc0 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:27:46 +0200 Subject: [PATCH 04/11] Remove useless profile from Cargo manifest --- Cargo.toml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e062245..0d18d98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,7 @@ [package] - -name = "tftpd" +name = "rustftp" version = "0.0.1" authors = [ "Adam Young " ] -[profile.dev] -opt-level = 0 # controls the `--opt-level` the compiler builds with -debug = true # controls whether the compiler passes `-g` -rpath = false # controls whether the compiler passes `-C rpath` -lto = false # controls `-C lto` for binaries and staticlibs -debug-assertions = true # controls whether debug assertions are enabled -codegen-units = 1 # - [dependencies] byteorder = "0.5.1" From 1de2b3532f12fba8287733b6a0d9d1723189bb0a Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:28:19 +0200 Subject: [PATCH 05/11] Bring name in line with repo name --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5aa2a96..762c37a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "tftpd" +name = "rustftp" version = "0.0.1" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", From 390e8846704083fef4f96c8cd7b035f02a294278 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:28:48 +0200 Subject: [PATCH 06/11] Update byteorder to latest version --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 762c37a..e060ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,14 @@ [[package]] name = "byteorder" -version = "0.5.3" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustftp" version = "0.0.1" dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" diff --git a/Cargo.toml b/Cargo.toml index 0d18d98..0ed532e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ version = "0.0.1" authors = [ "Adam Young " ] [dependencies] -byteorder = "0.5.1" +byteorder = "1.2" From eb27fd429e1d1c965dd7a693be492cd48600f666 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:29:27 +0200 Subject: [PATCH 07/11] Remove unused argument --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 60c48d9..3254556 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,7 @@ struct FileStream { } impl FileStream { - pub fn new(data: &mut Cursor<&Vec>, amt: &usize) -> FileStream { + pub fn new(data: &mut Cursor<&Vec>) -> FileStream { let mut parts = data.get_ref().as_slice()[2..].split(|b| *b == b'\x00'); let name = str::from_utf8(parts.next().unwrap()).unwrap(); let mode = str::from_utf8(parts.next().unwrap()).unwrap(); @@ -170,7 +170,7 @@ fn read_message(socket: &net::UdpSocket) { match opcode { 1 => { - let mut stream = FileStream::new(&mut rdr, &amt); + let mut stream = FileStream::new(&mut rdr); stream.send_chunk(1, &connection); file_streams.insert(src, stream); } From f4b9edaf8c027df516fcd85a4744f5f12f09b308 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:43:42 +0200 Subject: [PATCH 08/11] Simplify data handling some more --- src/main.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3254556..743fe3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,6 @@ use std::collections::HashMap; use std::env; use std::fs::File; use std::io::BufReader; -use std::io::Cursor; use std::io::prelude::*; use std::io::SeekFrom; use std::net; @@ -58,8 +57,8 @@ struct FileStream { } impl FileStream { - pub fn new(data: &mut Cursor<&Vec>) -> FileStream { - let mut parts = data.get_ref().as_slice()[2..].split(|b| *b == b'\x00'); + pub fn new(data: &[u8]) -> FileStream { + let mut parts = data[2..].split(|b| *b == b'\x00'); let name = str::from_utf8(parts.next().unwrap()).unwrap(); let mode = str::from_utf8(parts.next().unwrap()).unwrap(); println!("Request name: {:?}", name); @@ -152,25 +151,20 @@ fn read_message(socket: &net::UdpSocket) { let mut buf: [u8; 100] = [0; 100]; loop { - let result = socket.recv_from(&mut buf); - - match result { + match socket.recv_from(&mut buf) { Ok((amt, src)) => { - let data = Vec::from(&buf[0..amt]); let connection = Connection { socket: socket, src: &src, }; - let mut rdr = Cursor::new(&data); - if amt < 2 { panic!("Not enough data in packet") } - let opcode = rdr.read_u16::().unwrap(); - + let data = &buf[..amt]; + let opcode = data[1]; match opcode { 1 => { - let mut stream = FileStream::new(&mut rdr); + let mut stream = FileStream::new(data); stream.send_chunk(1, &connection); file_streams.insert(src, stream); } @@ -179,7 +173,7 @@ fn read_message(socket: &net::UdpSocket) { 4 => { let stream = file_streams.get_mut(&src).unwrap(); if !stream.done { - let chunk = rdr.read_u16::().unwrap() + 1; + let chunk = (&data[2..]).read_u16::().unwrap() + 1; stream.send_chunk(chunk as u64, &connection); } } From cb1c8e417ccc2c09cf901f46820a681e8e5e2144 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:46:03 +0200 Subject: [PATCH 09/11] Remove unused function --- src/main.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 743fe3e..3540d85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -186,14 +186,6 @@ fn read_message(socket: &net::UdpSocket) { } } -pub fn send_response(socket: &net::UdpSocket, src: &net::SocketAddr, data: &Vec) { - let result = socket.send_to(&data, src); - match result { - Ok(amt) => println!("Sent {} bytes", amt), - Err(err) => panic!("Write error: {}", err), - } -} - pub fn listen(listen_on: net::SocketAddr) { let socket = socket(listen_on); read_message(&socket) From ba063455ad021ea6565bf7bdf7609bbd941b98bc Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:46:20 +0200 Subject: [PATCH 10/11] Remove unused pub declarations --- src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3540d85..44bdd15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,14 +33,14 @@ struct Connection<'a> { } impl<'a> Connection<'a> { - pub fn send_response(&self, data: &[u8]) { + fn send_response(&self, data: &[u8]) { let result = self.socket.send_to(data, &self.src); match result { Ok(_) => {} Err(err) => panic!("Write error: {}", err), } } - pub fn send_error(&self, code: u16, err: &str) { + fn send_error(&self, code: u16, err: &str) { let mut message = Vec::new(); message.push((code >> 8) as u8); message.push(code as u8); @@ -57,7 +57,7 @@ struct FileStream { } impl FileStream { - pub fn new(data: &[u8]) -> FileStream { + fn new(data: &[u8]) -> FileStream { let mut parts = data[2..].split(|b| *b == b'\x00'); let name = str::from_utf8(parts.next().unwrap()).unwrap(); let mode = str::from_utf8(parts.next().unwrap()).unwrap(); @@ -90,7 +90,7 @@ impl FileStream { }; } - pub fn send_chunk(&mut self, chunk: u64, connection: &Connection) { + fn send_chunk(&mut self, chunk: u64, connection: &Connection) { if chunk > self.chunks { let end = [0u8, 3, (chunk >> 8) as u8, chunk as u8]; connection.send_response(&end); @@ -186,7 +186,7 @@ fn read_message(socket: &net::UdpSocket) { } } -pub fn listen(listen_on: net::SocketAddr) { +fn listen(listen_on: net::SocketAddr) { let socket = socket(listen_on); read_message(&socket) } From fcd81c29fb84d660daedc2d88f2b0b826ac66378 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 10 Apr 2018 16:51:09 +0200 Subject: [PATCH 11/11] Inline top-level functions --- src/main.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 44bdd15..3f5691a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,19 +133,6 @@ impl FileStream { } } -fn socket(listen_on: net::SocketAddr) -> net::UdpSocket { - let attempt = net::UdpSocket::bind(listen_on); - let socket; - match attempt { - Ok(sock) => { - println!("Bound socket to {}", listen_on); - socket = sock; - } - Err(err) => panic!("Could not bind: {}", err), - } - socket -} - fn read_message(socket: &net::UdpSocket) { let mut file_streams = HashMap::new(); @@ -186,13 +173,13 @@ fn read_message(socket: &net::UdpSocket) { } } -fn listen(listen_on: net::SocketAddr) { - let socket = socket(listen_on); - read_message(&socket) -} - fn main() { let ip = net::Ipv4Addr::new(0, 0, 0, 0); - let listen_addr = net::SocketAddrV4::new(ip, 69); - listen(net::SocketAddr::V4(listen_addr)); + let addr = net::SocketAddr::V4(net::SocketAddrV4::new(ip, 69)); + let sock = match net::UdpSocket::bind(addr) { + Ok(sock) => sock, + Err(err) => panic!("Could not bind: {}", err), + }; + println!("Bound socket to {}", addr); + read_message(&sock) }