1use crate::ValidStr;
24use packet::{
25 BufferView, FragmentedBytesMut, InnerPacketBuilder, NestablePacketBuilder, PacketBuilder,
26 PacketConstraints, ParsablePacket, ParseMetadata, SerializationContext, SerializeTarget,
27};
28use std::io::Write as _;
29use std::num::NonZeroU16;
30use std::str::FromStr;
31use thiserror::Error;
32use witness::NonEmptyValidStr;
33use zerocopy::byteorder::network_endian::U16;
34use zerocopy::{
35 FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut, Unaligned,
36};
37
38pub const OUTGOING_PORT: NonZeroU16 = NonZeroU16::new(33340).unwrap();
40
41pub const INCOMING_PORT: NonZeroU16 = NonZeroU16::new(33341).unwrap();
43
44pub const DEFAULT_BLOCK_SIZE_OPTION: u16 = 512;
48
49pub const DEFAULT_WINDOW_SIZE_OPTION: u16 = 512;
53
54pub const DEFAULT_TIMEOUT_SECS_OPTION: u16 = 1;
56
57mod witness {
58 use crate::ValidStr;
59
60 #[derive(Debug)]
62 pub(super) struct NonEmptyValidStr<B: zerocopy::SplitByteSlice>(ValidStr<B>);
63
64 impl<B: zerocopy::SplitByteSlice> NonEmptyValidStr<B> {
65 pub(super) fn new(str: ValidStr<B>) -> Option<Self> {
67 if str.as_str().is_empty() { None } else { Some(Self(str)) }
68 }
69 }
70
71 impl<B: zerocopy::SplitByteSlice> std::ops::Deref for NonEmptyValidStr<B> {
72 type Target = ValidStr<B>;
73 fn deref(&self) -> &Self::Target {
74 let Self(b) = self;
75 b
76 }
77 }
78}
79
80#[derive(Debug, Copy, Clone, Eq, PartialEq)]
84pub enum TftpError {
85 Undefined,
86 FileNotFound,
87 AccessViolation,
88 DiskFull,
89 IllegalOperation,
90 UnknownTransferId,
91 FileAlreadyExists,
92 NoSuchUser,
93 BadOptions,
97 Busy,
103 Unknown(u16),
104}
105
106impl Into<u16> for TftpError {
107 fn into(self) -> u16 {
108 match self {
109 TftpError::Undefined => 0,
110 TftpError::FileNotFound => 1,
111 TftpError::AccessViolation => 2,
112 TftpError::DiskFull => 3,
113 TftpError::IllegalOperation => 4,
114 TftpError::UnknownTransferId => 5,
115 TftpError::FileAlreadyExists => 6,
116 TftpError::NoSuchUser => 7,
117 TftpError::BadOptions => 8,
118 TftpError::Busy => 0x143,
119 TftpError::Unknown(v) => v,
120 }
121 }
122}
123
124#[derive(Debug, Eq, PartialEq, Clone)]
126pub enum StringField {
127 OptionName,
128 OptionValue,
129 Filename,
130 TransferMode,
131}
132
133#[derive(Debug, Eq, PartialEq, Clone, Error)]
135pub enum ParseError {
136 #[error("invalid message length")]
137 InvalidLength,
138 #[error("invalid empty string for {0:?}")]
139 EmptyStringValue(StringField),
140 #[error("failed to parse string: {0}")]
141 BadString(crate::ValidStrError),
142 #[error("bad option `{name} = {value}`")]
143 BadOption { name: String, value: String },
144 #[error("bad value for option `{name}`: {error}")]
145 BadOptionValue { name: String, error: std::num::ParseIntError },
146 #[error("unrecognized transfer mode `{0}`")]
147 UnrecognizedTransferMode(String),
148 #[error("invalid opcode: {0}")]
149 InvalidOpcode(u16),
150 #[error("too many options, dropped {0:?}")]
151 TooManyOptions(Forceable<TftpOption>),
152}
153
154impl From<u16> for TftpError {
155 fn from(value: u16) -> Self {
156 match value {
157 0 => TftpError::Undefined,
158 1 => TftpError::FileNotFound,
159 2 => TftpError::AccessViolation,
160 3 => TftpError::DiskFull,
161 4 => TftpError::IllegalOperation,
162 5 => TftpError::UnknownTransferId,
163 6 => TftpError::FileAlreadyExists,
164 7 => TftpError::NoSuchUser,
165 8 => TftpError::BadOptions,
166 0x143 => TftpError::Busy,
167 unknown => TftpError::Unknown(unknown),
168 }
169 }
170}
171
172#[derive(Debug, Copy, Clone, Eq, PartialEq)]
176pub enum Opcode {
177 ReadRequest,
178 WriteRequest,
179 Data,
180 Ack,
181 Error,
182 OptionAck,
186}
187
188impl Into<u16> for Opcode {
189 fn into(self) -> u16 {
190 match self {
191 Opcode::ReadRequest => 1,
192 Opcode::WriteRequest => 2,
193 Opcode::Data => 3,
194 Opcode::Ack => 4,
195 Opcode::Error => 5,
196 Opcode::OptionAck => 6,
197 }
198 }
199}
200
201impl TryFrom<u16> for Opcode {
202 type Error = ParseError;
203
204 fn try_from(value: u16) -> Result<Self, ParseError> {
205 match value {
206 1 => Ok(Opcode::ReadRequest),
207 2 => Ok(Opcode::WriteRequest),
208 3 => Ok(Opcode::Data),
209 4 => Ok(Opcode::Ack),
210 5 => Ok(Opcode::Error),
211 6 => Ok(Opcode::OptionAck),
212 opcode => Err(ParseError::InvalidOpcode(opcode)),
213 }
214 }
215}
216
217#[derive(Debug, Clone, Eq, PartialEq)]
220pub struct Forceable<T> {
221 pub value: T,
222 pub forced: bool,
223}
224
225#[derive(Debug, Default)]
227pub struct OptionCollection(arrayvec::ArrayVec<Forceable<TftpOption>, MAX_OPTIONS>);
228
229impl OptionCollection {
230 pub fn iter(&self) -> impl Iterator<Item = &Forceable<TftpOption>> {
232 let Self(this) = self;
233 this.iter()
234 }
235
236 pub fn try_push(&mut self, option: Forceable<TftpOption>) -> Result<(), Forceable<TftpOption>> {
241 let Self(this) = self;
242 this.try_push(option).map_err(|e| e.element())
243 }
244
245 pub fn serialized_len(&self) -> usize {
247 self.iter().map(|o| o.serialized_len()).sum()
248 }
249
250 fn parse<B: SplitByteSlice, BV: BufferView<B>>(buffer: &mut BV) -> Result<Self, ParseError> {
251 let Self(mut vec) = Self::default();
255 while !buffer.is_empty() {
256 let name = NonEmptyValidStr::new(
257 ValidStr::new_null_terminated_from_buffer(buffer).map_err(ParseError::BadString)?,
258 )
259 .ok_or(ParseError::EmptyStringValue(StringField::OptionName))?;
260 let value = NonEmptyValidStr::new(
261 ValidStr::new_null_terminated_from_buffer(buffer).map_err(ParseError::BadString)?,
262 )
263 .ok_or(ParseError::EmptyStringValue(StringField::OptionValue))?;
264
265 let option = TftpOption::parse(name.as_ref(), value.as_ref())?;
266 vec.try_push(option).map_err(|e| ParseError::TooManyOptions(e.element()))?;
267 }
268 Ok(Self(vec))
269 }
270
271 fn serialize<B: SplitByteSliceMut, BV: BufferView<B>>(&self, buffer: &mut BV) {
272 self.iter().for_each(|v| v.serialize(buffer))
273 }
274
275 pub fn collect(&self) -> AllOptions {
277 self.iter().cloned().collect()
278 }
279}
280
281impl std::iter::FromIterator<Forceable<TftpOption>> for OptionCollection {
282 fn from_iter<T: IntoIterator<Item = Forceable<TftpOption>>>(iter: T) -> Self {
283 Self(iter.into_iter().collect())
284 }
285}
286
287#[derive(Default, Eq, PartialEq, Debug)]
289pub struct AllOptions {
290 pub transfer_size: Option<Forceable<u64>>,
291 pub window_size: Option<Forceable<u16>>,
292 pub timeout: Option<Forceable<u8>>,
293 pub block_size: Option<Forceable<u16>>,
294}
295
296impl std::iter::FromIterator<Forceable<TftpOption>> for AllOptions {
301 fn from_iter<T: IntoIterator<Item = Forceable<TftpOption>>>(iter: T) -> Self {
302 iter.into_iter().fold(Self::default(), |mut all_options, Forceable { value, forced }| {
303 match value {
304 TftpOption::TransferSize(value) => {
305 all_options.transfer_size = Some(Forceable { value, forced })
306 }
307 TftpOption::BlockSize(value) => {
308 all_options.block_size = Some(Forceable { value, forced })
309 }
310 TftpOption::Timeout(value) => {
311 all_options.timeout = Some(Forceable { value, forced })
312 }
313 TftpOption::WindowSize(value) => {
314 all_options.window_size = Some(Forceable { value, forced })
315 }
316 }
317 all_options
318 })
319 }
320}
321
322#[derive(Debug)]
324pub struct RequestBody<B: SplitByteSlice> {
325 filename: NonEmptyValidStr<B>,
326 mode: TftpMode,
327 options: OptionCollection,
328}
329
330impl<B> RequestBody<B>
331where
332 B: SplitByteSlice,
333{
334 fn parse<BV: BufferView<B>>(buffer: &mut BV) -> Result<Self, ParseError> {
335 let filename = ValidStr::new_null_terminated_from_buffer(buffer)
336 .map_err(ParseError::BadString)
337 .and_then(|s| {
338 NonEmptyValidStr::new(s).ok_or(ParseError::EmptyStringValue(StringField::Filename))
339 })?;
340
341 let mode = TftpMode::try_from(
342 ValidStr::new_null_terminated_from_buffer(buffer)
343 .map_err(ParseError::BadString)
344 .and_then(|s| {
345 NonEmptyValidStr::new(s)
346 .ok_or(ParseError::EmptyStringValue(StringField::TransferMode))
347 })?
348 .as_ref(),
349 )?;
350 let options = OptionCollection::parse(buffer)?;
351 Ok(Self { filename, mode, options })
352 }
353
354 pub fn filename(&self) -> &str {
355 self.filename.as_ref()
356 }
357
358 pub fn mode(&self) -> TftpMode {
359 self.mode
360 }
361
362 pub fn options(&self) -> &OptionCollection {
363 &self.options
364 }
365}
366
367#[derive(Debug)]
369pub struct DataBody<B: SplitByteSlice> {
370 block: Ref<B, U16>,
371 payload: B,
372}
373
374impl<B> DataBody<B>
375where
376 B: SplitByteSlice,
377{
378 fn parse<BV: BufferView<B>>(buffer: &mut BV) -> Result<Self, ParseError> {
379 let block = buffer.take_obj_front::<U16>().ok_or(ParseError::InvalidLength)?;
380 let payload = buffer.take_rest_front();
381 Ok(Self { block, payload })
382 }
383
384 pub fn block(&self) -> u16 {
385 self.block.get()
386 }
387
388 pub fn payload(&self) -> &B {
389 &self.payload
390 }
391}
392
393#[derive(Debug)]
395pub struct AckBody<B: SplitByteSlice> {
396 block: Ref<B, U16>,
397}
398
399impl<B> AckBody<B>
400where
401 B: SplitByteSlice,
402{
403 fn parse<BV: BufferView<B>>(buffer: &mut BV) -> Result<Self, ParseError> {
404 let block = buffer.take_obj_front::<U16>().ok_or(ParseError::InvalidLength)?;
405 Ok(Self { block })
406 }
407
408 pub fn block(&self) -> u16 {
409 self.block.get()
410 }
411}
412
413#[derive(Debug)]
415pub struct ErrorBody<B: SplitByteSlice> {
416 error: TftpError,
417 msg: ValidStr<B>,
418}
419
420impl<B> ErrorBody<B>
421where
422 B: SplitByteSlice,
423{
424 fn parse<BV: BufferView<B>>(buffer: &mut BV) -> Result<Self, ParseError> {
425 let error =
426 TftpError::from(buffer.take_obj_front::<U16>().ok_or(ParseError::InvalidLength)?.get());
427 let msg =
428 ValidStr::new_null_terminated_from_buffer(buffer).map_err(ParseError::BadString)?;
429 Ok(Self { error, msg })
430 }
431
432 pub fn error(&self) -> TftpError {
433 self.error
434 }
435
436 pub fn message(&self) -> &str {
437 self.msg.as_ref()
438 }
439}
440
441#[derive(Debug)]
443pub struct OptionAckBody {
444 options: OptionCollection,
445}
446
447impl OptionAckBody {
448 fn parse<B: SplitByteSlice, BV: BufferView<B>>(buffer: &mut BV) -> Result<Self, ParseError> {
449 let options = OptionCollection::parse(buffer)?;
450 Ok(Self { options })
451 }
452
453 pub fn options(&self) -> &OptionCollection {
454 &self.options
455 }
456}
457
458#[derive(Debug)]
462pub enum TftpPacket<B: SplitByteSlice> {
463 ReadRequest(RequestBody<B>),
464 WriteRequest(RequestBody<B>),
465 Data(DataBody<B>),
466 Ack(AckBody<B>),
467 Error(ErrorBody<B>),
468 OptionAck(OptionAckBody),
469}
470
471impl<B: SplitByteSlice> TftpPacket<B> {
472 pub fn opcode(&self) -> Opcode {
474 match self {
475 TftpPacket::ReadRequest(_) => Opcode::ReadRequest,
476 TftpPacket::WriteRequest(_) => Opcode::WriteRequest,
477 TftpPacket::Data(_) => Opcode::Data,
478 TftpPacket::Ack(_) => Opcode::Ack,
479 TftpPacket::Error(_) => Opcode::Error,
480 TftpPacket::OptionAck(_) => Opcode::OptionAck,
481 }
482 }
483
484 pub fn into_read_request(self) -> Result<RequestBody<B>, Self> {
485 match self {
486 Self::ReadRequest(r) => Ok(r),
487 o => Err(o),
488 }
489 }
490
491 pub fn into_write_request(self) -> Result<RequestBody<B>, Self> {
492 match self {
493 Self::WriteRequest(r) => Ok(r),
494 o => Err(o),
495 }
496 }
497
498 pub fn into_data(self) -> Result<DataBody<B>, Self> {
499 match self {
500 Self::Data(r) => Ok(r),
501 o => Err(o),
502 }
503 }
504
505 pub fn into_ack(self) -> Result<AckBody<B>, Self> {
506 match self {
507 Self::Ack(r) => Ok(r),
508 o => Err(o),
509 }
510 }
511
512 pub fn into_error(self) -> Result<ErrorBody<B>, Self> {
513 match self {
514 Self::Error(r) => Ok(r),
515 o => Err(o),
516 }
517 }
518
519 pub fn into_oack(self) -> Result<OptionAckBody, Self> {
520 match self {
521 Self::OptionAck(r) => Ok(r),
522 o => Err(o),
523 }
524 }
525}
526
527impl<B> ParsablePacket<B, ()> for TftpPacket<B>
528where
529 B: SplitByteSlice,
530{
531 type Error = ParseError;
532
533 fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> Result<Self, ParseError> {
534 let opcode: Opcode =
535 buffer.take_obj_front::<MessageHead>().ok_or(ParseError::InvalidLength)?.opcode()?;
536 Ok(match opcode {
537 Opcode::ReadRequest => TftpPacket::ReadRequest(RequestBody::parse(&mut buffer)?),
538 Opcode::WriteRequest => TftpPacket::WriteRequest(RequestBody::parse(&mut buffer)?),
539 Opcode::Data => TftpPacket::Data(DataBody::parse(&mut buffer)?),
540 Opcode::Ack => TftpPacket::Ack(AckBody::parse(&mut buffer)?),
541 Opcode::Error => TftpPacket::Error(ErrorBody::parse(&mut buffer)?),
542 Opcode::OptionAck => TftpPacket::OptionAck(OptionAckBody::parse(&mut buffer)?),
543 })
544 }
545
546 fn parse_metadata(&self) -> ParseMetadata {
547 unimplemented!()
549 }
550}
551
552const OPT_NETASCII: unicase::Ascii<&'static str> = unicase::Ascii::new("NETASCII");
553const OPT_OCTET: unicase::Ascii<&'static str> = unicase::Ascii::new("OCTET");
554const OPT_MAIL: unicase::Ascii<&'static str> = unicase::Ascii::new("MAIL");
555
556#[derive(Debug, Copy, Clone, Eq, PartialEq)]
560pub enum TftpMode {
561 NETASCII,
562 OCTET,
563 MAIL,
564}
565
566impl TftpMode {
567 pub fn as_str(&self) -> &'static str {
568 match self {
569 TftpMode::NETASCII => OPT_NETASCII,
570 TftpMode::OCTET => OPT_OCTET,
571 TftpMode::MAIL => OPT_MAIL,
572 }
573 .into_inner()
574 }
575}
576
577impl Into<&'static str> for TftpMode {
578 fn into(self) -> &'static str {
579 self.as_str()
580 }
581}
582
583impl<'a> TryFrom<&'a str> for TftpMode {
584 type Error = ParseError;
585
586 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
587 let value = unicase::Ascii::new(value);
588 if value == OPT_NETASCII {
590 Ok(TftpMode::NETASCII)
591 } else if value == OPT_OCTET {
592 Ok(TftpMode::OCTET)
593 } else if value == OPT_MAIL {
594 Ok(TftpMode::MAIL)
595 } else {
596 Err(ParseError::UnrecognizedTransferMode(value.to_string()))
597 }
598 }
599}
600
601pub const MAX_OPTIONS: usize = 4;
603
604const OPT_TRANSFER_SIZE: unicase::Ascii<&'static str> = unicase::Ascii::new("TSIZE");
605const OPT_BLOCK_SIZE: unicase::Ascii<&'static str> = unicase::Ascii::new("BLKSIZE");
606const OPT_TIMEOUT: unicase::Ascii<&'static str> = unicase::Ascii::new("TIMEOUT");
607const OPT_WINDOWSIZE: unicase::Ascii<&'static str> = unicase::Ascii::new("WINDOWSIZE");
608
609#[derive(Debug, Eq, PartialEq, Clone)]
615pub enum TftpOption {
616 TransferSize(u64),
621 BlockSize(u16),
628 Timeout(u8),
634 WindowSize(u16),
641}
642
643impl TftpOption {
644 pub fn parse(option: &str, value: &str) -> Result<Forceable<Self>, ParseError> {
645 let (option, forced) = match option.chars().last() {
646 Some('!') => (&option[..option.len() - 1], true),
647 Some(_) | None => (option, false),
648 };
649 let option = unicase::Ascii::new(option);
650 let value = if option == OPT_TRANSFER_SIZE {
651 u64::from_str(value).map(|v| TftpOption::TransferSize(v))
652 } else if option == OPT_BLOCK_SIZE {
653 u16::from_str(value).map(|v| TftpOption::BlockSize(v))
654 } else if option == OPT_TIMEOUT {
655 u8::from_str(value).map(|v| TftpOption::Timeout(v))
656 } else if option == OPT_WINDOWSIZE {
657 u16::from_str(value).map(|v| TftpOption::WindowSize(v))
658 } else {
659 return Err(ParseError::BadOption {
660 name: option.to_string(),
661 value: value.to_string(),
662 });
663 }
664 .map_err(|error| ParseError::BadOptionValue { name: option.to_string(), error })?;
665
666 Ok(Forceable { value, forced })
667 }
668
669 fn get_option_and_value(&self) -> (unicase::Ascii<&'static str>, u64) {
670 match self {
671 TftpOption::TransferSize(v) => (OPT_TRANSFER_SIZE, *v),
672 TftpOption::BlockSize(v) => (OPT_BLOCK_SIZE, (*v).into()),
673 TftpOption::Timeout(v) => (OPT_TIMEOUT, (*v).into()),
674 TftpOption::WindowSize(v) => (OPT_WINDOWSIZE, (*v).into()),
675 }
676 }
677
678 pub const fn not_forced(self) -> Forceable<TftpOption> {
679 Forceable { value: self, forced: false }
680 }
681
682 pub const fn forced(self) -> Forceable<TftpOption> {
683 Forceable { value: self, forced: true }
684 }
685}
686
687impl Forceable<TftpOption> {
688 pub fn serialized_len(&self) -> usize {
693 let Forceable { value, forced } = self;
694 let (option, value) = value.get_option_and_value();
695 let forced = if *forced { 1 } else { 0 };
696
697 #[derive(Default)]
698 struct FormattedLen(usize);
699
700 impl std::io::Write for FormattedLen {
701 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
702 let Self(counter) = self;
703 *counter += buf.len();
704 Ok(buf.len())
705 }
706
707 fn flush(&mut self) -> std::io::Result<()> {
708 Ok(())
709 }
710 }
711
712 let mut value_len = FormattedLen::default();
713 let () =
714 std::write!(&mut value_len, "{}", value).expect("failed to serialize value to string");
715 let FormattedLen(value_len) = value_len;
716 forced + option.len() + value_len + 2
718 }
719
720 pub fn serialize<B: SplitByteSliceMut, BV: BufferView<B>>(&self, bv: &mut BV) {
722 let Forceable { value, forced } = self;
723 let (option, value) = value.get_option_and_value();
724 write_option_and_value(bv, option.as_ref(), *forced, value);
725 }
726}
727
728fn write_str<B, BV>(buff: &mut BV, v: &str)
729where
730 B: SplitByteSliceMut,
731 BV: BufferView<B>,
732{
733 write_str_forced(buff, v, false)
734}
735
736fn write_str_forced<B, BV>(buff: &mut BV, v: &str, forced: bool)
737where
738 B: SplitByteSliceMut,
739 BV: BufferView<B>,
740{
741 let extra = if forced { 2 } else { 1 };
742 let mut d = buff.take_front(v.len() + extra).unwrap();
743 let (data, end) = d.split_at_mut(v.len());
744 data.copy_from_slice(v.as_bytes());
745 if forced {
746 end[0] = '!' as u8;
747 end[1] = 0;
748 } else {
749 end[0] = 0;
750 }
751}
752
753fn write_option_and_value<B, BV, V>(buff: &mut BV, option: &str, forced: bool, value: V)
754where
755 B: SplitByteSliceMut,
756 BV: BufferView<B>,
757 V: std::fmt::Display,
758{
759 write_str_forced(buff, option, forced);
760
761 struct BVIoWriter<'a, B, BV>(&'a mut BV, std::marker::PhantomData<B>);
762
763 impl<'a, B, BV> std::io::Write for BVIoWriter<'a, B, BV>
764 where
765 BV: BufferView<B>,
766 B: SplitByteSliceMut,
767 {
768 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
769 let Self(bv, std::marker::PhantomData) = self;
770 let mut b = bv
771 .take_front(buf.len())
772 .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::OutOfMemory))?;
773 b.as_mut().copy_from_slice(buf);
774 Ok(b.len())
775 }
776
777 fn flush(&mut self) -> std::io::Result<()> {
778 Ok(())
779 }
780 }
781
782 std::write!(&mut BVIoWriter(buff, std::marker::PhantomData), "{}\0", value)
783 .unwrap_or_else(|e| panic!("failed to serialize {}: {:?}", value, e));
784}
785
786#[repr(C)]
787#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
788struct MessageHead {
789 opcode: U16,
790}
791
792impl MessageHead {
793 fn opcode(&self) -> Result<Opcode, ParseError> {
794 Opcode::try_from(self.opcode.get() & 0xff)
797 }
798
799 fn set_opcode(&mut self, opcode: Opcode) {
800 self.opcode.set(opcode.into());
801 }
802}
803
804#[derive(Debug, Copy, Clone)]
806pub enum TransferDirection {
807 Read,
808 Write,
809}
810
811#[derive(Debug)]
812pub struct TransferRequestBuilder<'a> {
814 direction: TransferDirection,
815 filename: &'a str,
816 mode: TftpMode,
817 options: OptionCollection,
818}
819
820impl<'a> TransferRequestBuilder<'a> {
821 pub fn new(direction: TransferDirection, filename: &'a str, mode: TftpMode) -> Self {
823 Self { direction, filename, mode, options: OptionCollection::default() }
824 }
825
826 pub fn new_with_options(
828 direction: TransferDirection,
829 filename: &'a str,
830 mode: TftpMode,
831 options: impl IntoIterator<Item = Forceable<TftpOption>>,
832 ) -> Self {
833 Self { direction, filename, mode, options: options.into_iter().collect() }
834 }
835
836 pub fn options_mut(&mut self) -> &mut OptionCollection {
837 &mut self.options
838 }
839}
840
841impl<'a> InnerPacketBuilder for TransferRequestBuilder<'a> {
842 fn bytes_len(&self) -> usize {
843 std::mem::size_of::<MessageHead>()
844 + self.filename.as_bytes().len()
845 + 1
846 + self.mode.as_str().as_bytes().len()
847 + 1
848 + self.options.serialized_len()
849 }
850
851 fn serialize(&self, mut buffer: &mut [u8]) {
852 let mut bv = crate::as_buffer_view_mut(&mut buffer);
853 bv.take_obj_front::<MessageHead>().unwrap().set_opcode(match self.direction {
854 TransferDirection::Read => Opcode::ReadRequest,
855 TransferDirection::Write => Opcode::WriteRequest,
856 });
857 write_str(&mut bv, self.filename.as_ref());
858 write_str(&mut bv, self.mode.as_str());
859 self.options.serialize(&mut bv);
860 }
861}
862
863#[derive(Debug)]
865pub struct DataPacketBuilder {
866 block: u16,
867}
868
869impl DataPacketBuilder {
870 pub fn new(block: u16) -> Self {
872 Self { block }
873 }
874}
875
876impl NestablePacketBuilder for DataPacketBuilder {
877 fn constraints(&self) -> PacketConstraints {
878 PacketConstraints::new(
879 std::mem::size_of::<MessageHead>() + std::mem::size_of::<U16>(),
880 0,
881 0,
882 std::u16::MAX.into(),
883 )
884 }
885}
886
887impl<C: SerializationContext> PacketBuilder<C> for DataPacketBuilder {
888 fn serialize(
889 &self,
890 _context: &mut C,
891 target: &mut SerializeTarget<'_>,
892 _body: FragmentedBytesMut<'_, '_>,
893 ) {
894 let mut bv = crate::as_buffer_view_mut(&mut target.header);
895 bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::Data);
896 bv.take_obj_front::<U16>().unwrap().set(self.block);
897 }
898}
899
900#[derive(Debug)]
902pub struct AckPacketBuilder {
903 block: u16,
904}
905
906impl AckPacketBuilder {
907 pub fn new(block: u16) -> Self {
909 Self { block }
910 }
911}
912
913impl InnerPacketBuilder for AckPacketBuilder {
914 fn bytes_len(&self) -> usize {
915 std::mem::size_of::<MessageHead>() + std::mem::size_of::<U16>()
916 }
917
918 fn serialize(&self, mut buffer: &mut [u8]) {
919 let mut bv = crate::as_buffer_view_mut(&mut buffer);
920 bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::Ack);
921 bv.take_obj_front::<U16>().unwrap().set(self.block);
922 }
923}
924
925#[derive(Debug)]
927pub struct ErrorPacketBuilder<'a> {
928 error: TftpError,
929 msg: &'a str,
930}
931
932impl<'a> ErrorPacketBuilder<'a> {
933 pub fn new(error: TftpError, msg: &'a str) -> Self {
935 Self { error, msg }
936 }
937}
938
939impl<'a> InnerPacketBuilder for ErrorPacketBuilder<'a> {
940 fn bytes_len(&self) -> usize {
941 std::mem::size_of::<MessageHead>()
942 + std::mem::size_of::<U16>()
943 + self.msg.as_bytes().len()
944 + 1
945 }
946
947 fn serialize(&self, mut buffer: &mut [u8]) {
948 let mut bv = crate::as_buffer_view_mut(&mut buffer);
949 bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::Error);
950 bv.take_obj_front::<U16>().unwrap().set(self.error.into());
951 write_str(&mut bv, self.msg);
952 }
953}
954
955#[derive(Debug, Default)]
957pub struct OptionAckPacketBuilder {
958 options: OptionCollection,
959}
960
961impl OptionAckPacketBuilder {
962 pub fn new_with(options: impl IntoIterator<Item = Forceable<TftpOption>>) -> Self {
964 Self { options: options.into_iter().collect() }
965 }
966
967 pub fn options_mut(&mut self) -> &mut OptionCollection {
968 &mut self.options
969 }
970}
971
972impl InnerPacketBuilder for OptionAckPacketBuilder {
973 fn bytes_len(&self) -> usize {
974 std::mem::size_of::<MessageHead>() + self.options.serialized_len()
975 }
976
977 fn serialize(&self, mut buffer: &mut [u8]) {
978 let mut bv = crate::as_buffer_view_mut(&mut buffer);
979 bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::OptionAck);
980 self.options.serialize(&mut bv);
981 }
982}
983
984#[cfg(test)]
985mod tests {
986 use super::*;
987 use packet::{NoOpSerializationContext, ParseBuffer as _, Serializer as _};
988
989 const FILENAME: &'static str = "filename";
990
991 #[test]
992 fn test_read_request() {
993 let mut req =
994 TransferRequestBuilder::new(TransferDirection::Read, FILENAME, TftpMode::OCTET)
995 .into_serializer()
996 .serialize_vec_outer(&mut NoOpSerializationContext)
997 .unwrap_or_else(|_| panic!("failed to serialize"));
998 let body = match req.parse::<TftpPacket<_>>().expect("failed to parse") {
999 TftpPacket::ReadRequest(b) => b,
1000 p => panic!("unexpected packet {:?}", p),
1001 };
1002 assert_eq!(body.filename(), FILENAME);
1003 assert_eq!(body.mode(), TftpMode::OCTET);
1004 assert!(body.options().iter().next().is_none());
1005 }
1006
1007 #[test]
1008 fn test_write_request() {
1009 let mut req =
1010 TransferRequestBuilder::new(TransferDirection::Write, FILENAME, TftpMode::OCTET)
1011 .into_serializer()
1012 .serialize_vec_outer(&mut NoOpSerializationContext)
1013 .unwrap_or_else(|_| panic!("failed to serialize"));
1014 let body = match req.parse::<TftpPacket<_>>().expect("failed to parse") {
1015 TftpPacket::WriteRequest(b) => b,
1016 p => panic!("unexpected packet {:?}", p),
1017 };
1018 assert_eq!(body.filename(), FILENAME);
1019 assert_eq!(body.mode(), TftpMode::OCTET);
1020 assert!(body.options().iter().next().is_none());
1021 }
1022
1023 #[test]
1024 fn test_data() {
1025 let data: Vec<_> = std::iter::successors(Some(0u8), |v| Some(*v + 1)).take(128).collect();
1026 let mut ser = DataPacketBuilder::new(123)
1027 .wrap_body((&data[..]).into_serializer())
1028 .serialize_vec_outer(&mut NoOpSerializationContext)
1029 .unwrap_or_else(|_| panic!("failed to serialize"));
1030 let body = match ser.parse::<TftpPacket<_>>().expect("failed to parse") {
1031 TftpPacket::Data(b) => b,
1032 p => panic!("unexpected packet {:?}", p),
1033 };
1034 assert_eq!(body.block(), 123);
1035 assert_eq!(body.payload().as_ref(), &data[..]);
1036 }
1037
1038 #[test]
1039 fn test_error() {
1040 const ERR_STR: &str = "ERROR";
1041 let mut err = ErrorPacketBuilder::new(TftpError::FileNotFound, ERR_STR)
1042 .into_serializer()
1043 .serialize_vec_outer(&mut NoOpSerializationContext)
1044 .unwrap_or_else(|_| panic!("failed to serialize"));
1045 let body = match err.parse::<TftpPacket<_>>().expect("failed to parse") {
1046 TftpPacket::Error(b) => b,
1047 p => panic!("unexpected packet {:?}", p),
1048 };
1049 assert_eq!(body.error(), TftpError::FileNotFound);
1050 assert_eq!(body.message(), ERR_STR);
1051 }
1052
1053 #[test]
1054 fn test_option_ack() {
1055 let builder = OptionAckPacketBuilder::new_with([
1056 TftpOption::WindowSize(10).not_forced(),
1057 TftpOption::BlockSize(35).not_forced(),
1058 TftpOption::Timeout(1).forced(),
1059 TftpOption::TransferSize(400).forced(),
1060 ]);
1061 let mut oack = builder
1062 .into_serializer()
1063 .serialize_vec_outer(&mut NoOpSerializationContext)
1064 .unwrap_or_else(|_| panic!("failed to serialize"));
1065 let body = match oack.parse::<TftpPacket<_>>().expect("failed to parse") {
1066 TftpPacket::OptionAck(b) => b,
1067 p => panic!("unexpected packet {:?}", p),
1068 };
1069 let AllOptions { window_size, block_size, timeout, transfer_size } =
1070 body.options().collect();
1071 assert_eq!(window_size, Some(Forceable { value: 10, forced: false }));
1072 assert_eq!(block_size, Some(Forceable { value: 35, forced: false }));
1073 assert_eq!(timeout, Some(Forceable { value: 1, forced: true }));
1074 assert_eq!(transfer_size, Some(Forceable { value: 400, forced: true }));
1075 }
1076
1077 #[test]
1078 fn test_ack() {
1079 let mut ack = AckPacketBuilder::new(123)
1080 .into_serializer()
1081 .serialize_vec_outer(&mut NoOpSerializationContext)
1082 .unwrap_or_else(|_| panic!("failed to serialize"));
1083 let body = match ack.parse::<TftpPacket<_>>().expect("failed to parse") {
1084 TftpPacket::Ack(b) => b,
1085 p => panic!("unexpected packet {:?}", p),
1086 };
1087 assert_eq!(body.block(), 123);
1088 }
1089
1090 #[test]
1091 fn test_transfer_request_options() {
1092 let builder = TransferRequestBuilder::new_with_options(
1093 TransferDirection::Read,
1094 FILENAME,
1095 TftpMode::OCTET,
1096 [
1097 TftpOption::WindowSize(10).not_forced(),
1098 TftpOption::BlockSize(35).not_forced(),
1099 TftpOption::Timeout(1).forced(),
1100 TftpOption::TransferSize(400).forced(),
1101 ],
1102 );
1103 let mut req = builder
1104 .into_serializer()
1105 .serialize_vec_outer(&mut NoOpSerializationContext)
1106 .unwrap_or_else(|_| panic!("failed to serialize"));
1107 let body = match req.parse::<TftpPacket<_>>().expect("failed to parse") {
1108 TftpPacket::ReadRequest(b) => b,
1109 p => panic!("unexpected packet {:?}", p),
1110 };
1111 let AllOptions { window_size, block_size, timeout, transfer_size } =
1112 body.options().collect();
1113 assert_eq!(window_size, Some(Forceable { value: 10, forced: false }));
1114 assert_eq!(block_size, Some(Forceable { value: 35, forced: false }));
1115 assert_eq!(timeout, Some(Forceable { value: 1, forced: true }));
1116 assert_eq!(transfer_size, Some(Forceable { value: 400, forced: true }));
1117 }
1118}