aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReiner Herrmann <reiner@reiner-h.de>2019-02-24 14:47:11 +0100
committerReiner Herrmann <reiner@reiner-h.de>2019-02-24 14:47:11 +0100
commit14ef6bbb230051f0ba5c6ed7a3211e34e0986f6f (patch)
treecef9d5efb07812bc374b2a506620f0db796121b0
parent66b9f0e2590831548333e2166fe289dfd013f5c3 (diff)
Initial tftpd implementation with RRQ support
-rw-r--r--Cargo.toml4
-rw-r--r--src/main.rs3
-rw-r--r--src/tftpd.rs105
3 files changed, 109 insertions, 3 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 14c3e2b..17bc89d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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]);
+ }
+}