diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e060ae1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,14 @@ +[[package]] +name = "byteorder" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustftp" +version = "0.0.1" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" diff --git a/Cargo.toml b/Cargo.toml index e062245..0ed532e 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" +byteorder = "1.2" diff --git a/src/main.rs b/src/main.rs index 25e84ca..3f5691a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,204 +13,173 @@ // 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::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> { + 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), } } + 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 { + 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); + println!("Mode: {:?}", mode); -impl FileStream{ - pub fn new(data: &mut Cursor<&Vec>, amt: &usize,) -> FileStream { + let mut abs_path = env::current_dir().unwrap(); + abs_path.push(name); + println!("Absolute path: {:?}", abs_path.display()); - 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 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) + 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 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]); + + 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; } - connection.send_response(message); - println!("sending block : {}", chunk); - } -} -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) + 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); + } } - 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{ - let result = socket.recv_from(&mut buf); - - match result { + loop { + 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{ + let connection = Connection { + socket: socket, + src: &src, + }; + 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 => { - file_streams.insert(src, handle_read_request( - &mut rdr, &amt, &connection)); - }, + let mut stream = FileStream::new(data); + 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 = (&data[2..]).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) { - 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) -} - -fn main(){ - - - let ip = net::Ipv4Addr::new(127, 0, 0, 1); - let listen_addr = net::SocketAddrV4::new(ip, 8888); - listen(net::SocketAddr::V4(listen_addr)); +fn main() { + let ip = net::Ipv4Addr::new(0, 0, 0, 0); + 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) }