Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 2 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
[package]

name = "tftpd"
name = "rustftp"
version = "0.0.1"
authors = [ "Adam Young <adam@younglogic.com>" ]

[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"
257 changes: 113 additions & 144 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,204 +13,173 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.


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<u8>) {
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<File>,
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<u8>>, 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<u8> =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<u8> =Vec::new();
message.push(0);
message.push(3);
message.write_u16::<BigEndian>(*chunk).unwrap();

for i in 0..bytes_read{
message.push(content[i]);

let mut buf = Vec::with_capacity(516);
buf.write_u16::<BigEndian>(3).unwrap();
buf.write_u16::<BigEndian>(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<u8>>, 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::<BigEndian>().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::<BigEndian>().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::<BigEndian>().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<u8>) {
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)
}