aboutsummaryrefslogtreecommitdiff
path: root/src/tftp.rs
diff options
context:
space:
mode:
authorReiner Herrmann <reiner@reiner-h.de>2019-02-27 20:10:09 +0100
committerReiner Herrmann <reiner@reiner-h.de>2019-02-27 20:10:09 +0100
commitc659a6d621a4171d03f42b539b4b1a8d00b8b194 (patch)
tree1a43eac2dd0764b295ed87515bfd35f25c53314f /src/tftp.rs
parent7cf4ef5de82cf01aaeb443b35d4794e175bd3dc2 (diff)
Implement Blocksize Option (RFC 2348)
Diffstat (limited to 'src/tftp.rs')
-rw-r--r--src/tftp.rs64
1 files changed, 42 insertions, 22 deletions
diff --git a/src/tftp.rs b/src/tftp.rs
index 67dcd1a..3319bc4 100644
--- a/src/tftp.rs
+++ b/src/tftp.rs
@@ -12,15 +12,25 @@ use std::io;
use std::io::prelude::*;
pub struct TftpOptions {
-
+ blksize: usize,
}
pub struct Tftp {
+ options: TftpOptions,
+}
+
+fn default_options() -> TftpOptions {
+ TftpOptions {
+ blksize: 512,
+ }
}
impl Tftp {
+
pub fn new() -> Tftp {
- Tftp{}
+ Tftp{
+ options: default_options(),
+ }
}
fn get_tftp_str(&self, buf: &[u8]) -> Option<(String, usize)> {
@@ -60,6 +70,10 @@ impl Tftp {
fn ack_options(&self, sock: &UdpSocket, options: &HashMap<String, String>, ackwait: bool) -> Result<(), io::Error> {
if options.is_empty() {
+ if !ackwait {
+ /* it's a WRQ, send normal ack to start transfer */
+ self.send_ack(&sock, 0)?;
+ }
return Ok(())
}
@@ -88,13 +102,19 @@ impl Tftp {
Err(io::Error::new(io::ErrorKind::TimedOut, "ack timeout"))
}
- pub fn init_tftp_options(&self, sock: &UdpSocket, options: &mut HashMap<String, String>, ackwait: bool) -> Result<TftpOptions, io::Error> {
- let tftpopts = TftpOptions {};
+ pub fn init_tftp_options(&mut self, sock: &UdpSocket, options: &mut HashMap<String, String>, ackwait: bool) -> Result<(), io::Error> {
+ self.options = default_options();
- options.retain(|key, _val| {
+ options.retain(|key, val| {
match key.as_str() {
- "placeholder_option" => {
- true
+ "blksize" => {
+ match val.parse() {
+ Ok(b) if b >= 8 && b <= 65464 => {
+ self.options.blksize = b;
+ true
+ }
+ _ => false,
+ }
}
_ => false
}
@@ -102,7 +122,7 @@ impl Tftp {
self.ack_options(&sock, &options, ackwait)?;
- return Ok(tftpopts);
+ return Ok(());
}
fn parse_options(&self, buf: &[u8]) -> HashMap<String, String> {
@@ -189,7 +209,7 @@ impl Tftp {
let mut block_nr: u16 = 1;
loop {
- let mut filebuf = [0; 512];
+ let mut filebuf = vec![0; self.options.blksize];
let len = match file.read(&mut filebuf) {
Ok(n) => n,
Err(ref error) if error.kind() == io::ErrorKind::Interrupted => continue, /* retry */
@@ -222,7 +242,7 @@ impl Tftp {
return Err(io::Error::new(io::ErrorKind::TimedOut, "ack timeout"))
}
- if len < 512 {
+ if len < self.options.blksize {
/* this was the last block */
break;
}
@@ -242,26 +262,26 @@ impl Tftp {
Err(_) => return Err(io::Error::new(io::ErrorKind::PermissionDenied, "permission denied")),
};
- let mut block_nr = 0;
+ let mut block_nr: u16 = 1;
loop {
- let mut buf = [0; 1024];
+ let mut buf = vec![0; 4 + self.options.blksize + 1]; // +1 for later size check
let mut len = 0;
for _ in 1..5 {
- self.send_ack(&sock, block_nr)?;
len = match sock.recv(&mut buf) {
Ok(n) => n,
Err(ref error) if [io::ErrorKind::WouldBlock, io::ErrorKind::TimedOut].contains(&error.kind()) => {
- /* re-ack and try to recv again */
+ /* re-ack previous and try to recv again */
+ self.send_ack(&sock, block_nr - 1)?;
continue;
}
Err(err) => return Err(err),
};
break;
}
- if len > 516 || len < 4 {
- /* max size: 2 + 2 + 512 */
+ if len < 4 || len > 4 + self.options.blksize {
+ /* max size: 2 + 2 + blksize */
return Err(io::Error::new(io::ErrorKind::InvalidInput, "unexpected size"));
}
@@ -269,25 +289,25 @@ impl Tftp {
3 /* DATA */ => (),
_ => return Err(io::Error::new(io::ErrorKind::Other, "unexpected opcode")),
};
- let nr = u16::from_be_bytes([buf[2], buf[3]]);
- if nr != block_nr.wrapping_add(1) {
+ if u16::from_be_bytes([buf[2], buf[3]]) != block_nr {
/* already received or packets were missed, re-acknowledge */
+ self.send_ack(&sock, block_nr - 1)?;
continue;
}
- block_nr = nr;
let databuf = &buf[4..len];
file.write_all(databuf)?;
- if len < 516 {
+ self.send_ack(&sock, block_nr)?;
+ block_nr = block_nr.wrapping_add(1);
+
+ if len < 4 + self.options.blksize {
break;
}
}
file.flush()?;
- self.send_ack(&sock, block_nr)?;
-
Ok(())
}