Skip to content

Commit fd73775

Browse files
committed
Refactor option negotatiation process
Encapsulate option negotiation logic inside a specific function. Send timeout error after negotiation failure.
1 parent de6a61a commit fd73775

2 files changed

Lines changed: 74 additions & 29 deletions

File tree

src/peer_handler/mod.rs

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::cursor::{BufferError, ReadCursor};
1+
use crate::cursor::ReadCursor;
22
use crate::datagram_stream::DatagramStream;
33
use crate::fs::{FileError, OpenedFile, Root};
44
use crate::local_fs::LocalRoot;
@@ -9,6 +9,7 @@ use crate::remote_fs::{Config, VirtualRootError};
99
use serde_json::Value;
1010
use std::collections::HashMap;
1111
use std::fmt::{Debug, Display, Formatter};
12+
use std::io;
1213
use std::net::{IpAddr, SocketAddr};
1314
use std::ops::DerefMut;
1415
use std::path::{Path, PathBuf};
@@ -100,13 +101,6 @@ impl Window {
100101
}
101102
}
102103

103-
fn push_ack(window: &mut Window, oack: &OptionsAcknowledge) -> Result<usize, BufferError> {
104-
let buffer = window.buffer(0);
105-
let read_bytes = oack.serialize(buffer)?;
106-
buffer.truncate(read_bytes);
107-
Ok(read_bytes)
108-
}
109-
110104
async fn send_file(
111105
mut opened_file: Box<dyn OpenedFile>,
112106
datagram_stream: &DatagramStream,
@@ -524,6 +518,57 @@ async fn send_reliably(
524518
}
525519
Err(SendError::Timeout)
526520
}
521+
522+
async fn send_oack_reliably(
523+
oack: &OptionsAcknowledge,
524+
datagram_stream: &DatagramStream,
525+
ack_timeout: &AckTimeout,
526+
buffer: &mut [u8],
527+
) -> io::Result<()> {
528+
let oack_index = 0;
529+
let oack_size = match oack.serialize(buffer) {
530+
Ok(size) => size,
531+
Err(buffer_error) => {
532+
let tftp_error = TFTPError::new("OACK build error", UNDEFINED_ERROR);
533+
fire_error(tftp_error, datagram_stream, buffer).await;
534+
return Err(io::Error::other(format!(
535+
"Error building options: {buffer_error}"
536+
)));
537+
}
538+
};
539+
for attempt in 1..=SEND_ATTEMPTS {
540+
datagram_stream.send(&buffer[..oack_size]).await?;
541+
match read_acknowledge(datagram_stream, buffer, ack_timeout).await {
542+
Ok(ack_num) if ack_num == oack_index => return Ok(()),
543+
Ok(ack_num) => {
544+
let tftp_error = TFTPError::new("Unexpected non-zero ACK", UNDEFINED_ERROR);
545+
fire_error(tftp_error, datagram_stream, buffer).await;
546+
return Err(io::Error::other(format!(
547+
"Received unexpected ACK {ack_num} while expecting {oack_index}"
548+
)));
549+
}
550+
Err(RecvError::Timeout) => {
551+
eprintln!("Timeout waiting for ACK {oack_index}, attempt {attempt}");
552+
continue;
553+
}
554+
Err(RecvError::ClientError(code, string)) => {
555+
return Err(io::Error::other(format!(
556+
"Early termination while options negotiation [{code}] {string}"
557+
)));
558+
}
559+
Err(error) => {
560+
return Err(io::Error::other(format!("ACK read error: {:?}", error)));
561+
}
562+
}
563+
}
564+
let tftp_error = TFTPError::new("Send timeout occurred", UNDEFINED_ERROR);
565+
fire_error(tftp_error, datagram_stream, buffer).await;
566+
Err(io::Error::new(
567+
io::ErrorKind::TimedOut,
568+
format!("Timeout waiting for ACK {oack_index}"),
569+
))
570+
}
571+
527572
async fn negotiate_options(
528573
datagram_stream: &DatagramStream,
529574
opened_file: &mut Box<dyn OpenedFile>,
@@ -563,25 +608,14 @@ async fn negotiate_options(
563608
Default::default()
564609
}
565610
};
566-
let mut window = Window::new(block_size.get_size() as u16, window_size.get_size() as u16);
567-
if oack.has_options() {
568-
eprintln!("{datagram_stream}: {oack}");
569-
match push_ack(&mut window, &oack) {
570-
Ok(_) => {}
571-
Err(buffer_error) => {
572-
eprintln!("{datagram_stream}: Error building options: {buffer_error}");
573-
let tftp_error = TFTPError::new("OACK build error", UNDEFINED_ERROR);
574-
fire_error(tftp_error, datagram_stream, buffer).await;
575-
return None;
576-
}
577-
}
578-
if let Err(error) =
579-
send_reliably(&mut window, &ack_timeout, datagram_stream, buffer, 0, 1).await
580-
{
581-
eprintln!("{datagram_stream}: Error sending options: {error:?}");
582-
return None;
583-
}
611+
if oack.has_options()
612+
&& let Err(oack_negotiation_error) =
613+
send_oack_reliably(&oack, datagram_stream, &ack_timeout, buffer).await
614+
{
615+
eprintln!("{datagram_stream}: {oack_negotiation_error}");
616+
return None;
584617
};
618+
let window = Window::new(block_size.get_size() as u16, window_size.get_size() as u16);
585619
Some((window, ack_timeout))
586620
}
587621

tests/test_server.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ async fn change_timeout() {
294294
(Vec::new(), 0u64),
295295
(Vec::new(), 0u64),
296296
(Vec::new(), 0u64),
297+
(Vec::new(), 0u64),
297298
];
298299
let local_read_timeout = 2usize;
299300
let mut buffer = [0u8; _BUFFER_SIZE];
@@ -352,15 +353,25 @@ async fn change_timeout() {
352353
retry_buffers[3].1, 4
353354
);
354355
assert_eq!(
355-
retry_buffers[4].0, b"",
356+
retry_buffers[4].0, b"\x00\x05\x00\x00Send timeout occurred\x00",
356357
"5: Received: {:?}, Expected: {:?}",
357-
retry_buffers[4].0, b""
358+
retry_buffers[4].0, b"\x00\x05\x00\x00Send timeout occurred\x00"
358359
);
359360
assert_eq!(
360-
retry_buffers[4].1, 0,
361+
retry_buffers[4].1, 5,
361362
"5: Timestamp mismatch. Received: {}, Expected: {}",
362363
retry_buffers[4].1, 5
363364
);
365+
assert_eq!(
366+
retry_buffers[5].0, b"",
367+
"5: Received: {:?}, Expected: {:?}",
368+
retry_buffers[5].0, b""
369+
);
370+
assert_eq!(
371+
retry_buffers[5].1, 0,
372+
"5: Timestamp mismatch. Received: {}, Expected: {}",
373+
retry_buffers[5].1, 0
374+
);
364375
}
365376

366377
#[tokio::test(flavor = "current_thread")]

0 commit comments

Comments
 (0)