diff options
| author | Reiner Herrmann <reiner@reiner-h.de> | 2019-02-24 14:47:11 +0100 |
|---|---|---|
| committer | Reiner Herrmann <reiner@reiner-h.de> | 2019-02-24 14:47:11 +0100 |
| commit | 14ef6bbb230051f0ba5c6ed7a3211e34e0986f6f (patch) | |
| tree | cef9d5efb07812bc374b2a506620f0db796121b0 | |
| parent | 66b9f0e2590831548333e2166fe289dfd013f5c3 (diff) | |
Initial tftpd implementation with RRQ support
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/tftpd.rs | 105 |
3 files changed, 109 insertions, 3 deletions
@@ -5,3 +5,7 @@ authors = ["Reiner Herrmann <reiner@reiner-h.de>"] edition = "2018" [dependencies] + +[[bin]] +name = "rtftpd" +path = "src/tftpd.rs" diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/src/tftpd.rs b/src/tftpd.rs new file mode 100644 index 0000000..2237761 --- /dev/null +++ b/src/tftpd.rs @@ -0,0 +1,105 @@ +use std::net::{SocketAddr,UdpSocket}; +use std::fs::File; +use std::io::prelude::*; + +fn handle_wrq(_cl: &SocketAddr, _buf: &[u8]) { +} + +fn wait_for_ack(sock: &UdpSocket, expected_block: u16) { + let mut buf = [0; 4]; + sock.recv(&mut buf).expect("recv"); + + let opcode = u16::from_be_bytes([buf[0], buf[1]]); + let block_nr = u16::from_be_bytes([buf[2], buf[3]]); + + if opcode != 4 { + // error + } + if block_nr != expected_block { + // error + } +} + +fn send_file(cl: &SocketAddr, filename: &str) { + let mut file = File::open(filename).expect("open"); + + let socket = UdpSocket::bind("0.0.0.0:0").expect("bind"); + socket.connect(cl).expect("connect"); + let mut block_nr: u16 = 1; + + loop { + let mut filebuf = [0; 512]; + let n = file.read(&mut filebuf).expect("read"); + + let mut sendbuf = vec![0x00, 0x03]; // opcode + sendbuf.extend(block_nr.to_be_bytes().iter()); + sendbuf.extend(filebuf[0..n].iter()); + + socket.send(&sendbuf).expect("send"); + wait_for_ack(&socket, block_nr); + + if n < 512 { + /* this was the last block */ + break; + } + + /* increment with rollover on overflow */ + block_nr = block_nr.wrapping_add(1); + } +} + +fn handle_rrq(cl: &SocketAddr, buf: &[u8]) { + let mut iter = buf.iter(); + + let fname_len = iter.position(|&x| x == 0).expect("not found"); + let fname_begin = 0; + let fname_end = fname_begin + fname_len; + let filename = String::from_utf8(buf[fname_begin .. fname_end].to_vec()).expect("str"); + + let mode_len = iter.position(|&x| x == 0).expect("not found"); + let mode_begin = fname_end + 1; + let mode_end = mode_begin + mode_len; + let mode = String::from_utf8(buf[mode_begin .. mode_end].to_vec()).expect("str"); + let mode = mode.to_lowercase(); + + match mode.as_ref() { + "octet" => println!("octet mode"), + _ => handle_error(cl, 0, "Unsupported mode"), + } + + println!("Sending {} to {}", filename, cl); + send_file(&cl, &filename); +} + +fn handle_error(cl: &SocketAddr, code: u16, msg: &str) { + let socket = UdpSocket::bind("0.0.0.0:0").expect("bind"); + socket.connect(cl).expect("connect"); + + let mut buf = vec![0x00, 0x05]; // opcode + buf.extend(code.to_be_bytes().iter()); + buf.extend(msg.as_bytes()); + + socket.send(&buf).expect("send"); +} + +fn handle_client(cl: &SocketAddr, buf: &[u8]) { + let opcode = u16::from_be_bytes([buf[0], buf[1]]); + + match opcode { + 1 /* RRQ */ => handle_rrq(&cl, &buf[2..]), + 2 /* WRQ */ => handle_wrq(&cl, &buf[2..]), + 5 /* ERROR */ => println!("Received ERROR from {}", cl), + _ => handle_error(cl, 4, "Unexpected opcode"), + } +} + +fn main() { + let socket = UdpSocket::bind("127.0.0.1:12345").expect("bind"); + + loop { + let mut buf = [0; 2048]; + let (n, src) = socket.recv_from(&mut buf).expect("recv"); + + handle_client(&src, &buf[0..n]); + } +} |
