netsvc_proto/
tftp.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! TFTP library.
6//!
7//! Netsvc uses TFTP for file transfers. TFTP is defined in [RFC 1350]. Netsvc
8//! also incorporates the following extensions:
9//!  * Option extension [RFC 2347].
10//!  * Block size option [RFC 2348].
11//!  * Timeout interval, and transfer size options [RFC 2349].
12//!  * Windows size option [RFC 7440].
13//!
14//! Over the years netsvc also introduces some fuchsia-specific extensions,
15//! which are called out in documentation.
16//!
17//! [RFC 1350]: https://datatracker.ietf.org/doc/html/rfc1350
18//! [RFC 2347]: https://datatracker.ietf.org/doc/html/rfc2347
19//! [RFC 2348]: https://datatracker.ietf.org/doc/html/rfc2348
20//! [RFC 2349]: https://datatracker.ietf.org/doc/html/rfc2349
21//! [RFC 7440]: https://datatracker.ietf.org/doc/html/rfc7440
22
23use crate::ValidStr;
24use packet::{
25    BufferView, FragmentedBytesMut, InnerPacketBuilder, PacketBuilder, PacketConstraints,
26    ParsablePacket, ParseMetadata, 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
38/// The port netsvc uses to send TFTP traffic from.
39pub const OUTGOING_PORT: NonZeroU16 = NonZeroU16::new(33340).unwrap();
40
41/// The port netsvc uses to listen to TFTP traffic.
42pub const INCOMING_PORT: NonZeroU16 = NonZeroU16::new(33341).unwrap();
43
44/// The default block size option value, according to [RFC 1350].
45///
46/// [RFC 1350]: https://datatracker.ietf.org/doc/html/rfc1350
47pub const DEFAULT_BLOCK_SIZE_OPTION: u16 = 512;
48
49/// The default window size option value, according to [RFC 1350].
50///
51/// [RFC 1350]: https://datatracker.ietf.org/doc/html/rfc1350
52pub const DEFAULT_WINDOW_SIZE_OPTION: u16 = 512;
53
54/// The default timeout option value used in netsvc.
55pub const DEFAULT_TIMEOUT_SECS_OPTION: u16 = 1;
56
57mod witness {
58    use crate::ValidStr;
59
60    /// A witness type for non empty [`ValidStr`]s.
61    #[derive(Debug)]
62    pub(super) struct NonEmptyValidStr<B: zerocopy::SplitByteSlice>(ValidStr<B>);
63
64    impl<B: zerocopy::SplitByteSlice> NonEmptyValidStr<B> {
65        /// Creates a new [`NonEmptyValidStr`] iff `str` is not empty.
66        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/// Error codes defined in [RFC 1350 appendix I].
81///
82/// [RFC 1350 appendix I]: https://datatracker.ietf.org/doc/html/rfc1350#appendix-I
83#[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    /// Introduced in [RFC 2347].
94    ///
95    /// [RFC 2347]: https://datatracker.ietf.org/doc/html/rfc2347.
96    BadOptions,
97    /// Fuchsia-specific error code.
98    ///
99    /// BUSY is sent by a server as a response to a RRQ or WRQ, and indicates
100    /// that the server is unavailable to process the request at the moment
101    /// (but expects to be able to handle it at some time in the future).
102    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/// Fields that are parsed as strings.
125#[derive(Debug, Eq, PartialEq, Clone)]
126pub enum StringField {
127    OptionName,
128    OptionValue,
129    Filename,
130    TransferMode,
131}
132
133/// Kinds of observable [`ParseError`]s.
134#[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/// TFTP Opcodes as defined in [RFC 1350 section 5].
173///
174/// [RFC 1350 section 5]: https://datatracker.ietf.org/doc/html/rfc1350#section-5
175#[derive(Debug, Copy, Clone, Eq, PartialEq)]
176pub enum Opcode {
177    ReadRequest,
178    WriteRequest,
179    Data,
180    Ack,
181    Error,
182    /// Introduced in [RFC 2347].
183    ///
184    /// [RFC 2347]: https://datatracker.ietf.org/doc/html/rfc2347.
185    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/// Helper structure to encode forced values, a Fuchsia-specific extension to
218/// options.
219#[derive(Debug, Clone, Eq, PartialEq)]
220pub struct Forceable<T> {
221    pub value: T,
222    pub forced: bool,
223}
224
225/// A collection of options in a TFTP message.
226#[derive(Debug, Default)]
227pub struct OptionCollection(arrayvec::ArrayVec<Forceable<TftpOption>, MAX_OPTIONS>);
228
229impl OptionCollection {
230    /// Returns an iterator over the contained options.
231    pub fn iter(&self) -> impl Iterator<Item = &Forceable<TftpOption>> {
232        let Self(this) = self;
233        this.iter()
234    }
235
236    /// Pushes a new option.
237    ///
238    /// Returns an error if if this [`OptionCollection`] already contains
239    /// [`MAX_OPTIONS`].
240    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    /// Gets the total serialized length of the contained options.
246    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        // options always come at the end, we'll try to gather options until the
252        // buffer is exhausted or we've already gathered as many options as we
253        // can fit
254        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            let () = 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    /// Collects the containing options into [`AllOptions`].
276    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/// A container with all possible [`TftpOption`] values in a message.
288#[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
296/// Constructs an [`AllOptions`] from an iterator of [`Forceable<TftpOption>`].
297///
298/// If the same option appears more than once in the iterator, the later value
299/// is kept.
300impl 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/// The body of a Read or Write request.
323#[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/// The body of a data message.
368#[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/// The body of an Ack message.
394#[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/// The body of an error message.
414#[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/// The body of an option ack (OACK) message.
442#[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/// A TFTP packet.
459///
460/// Implements [`ParsablePacket`] to parse from wire representation.
461#[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    /// Gets the opcode for the packet.
473    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        // ParseMetadata is only needed if we need to undo parsing.
548        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/// TFTP transfer modes defined in [RFC 1350].
557///
558/// [RFC 1350]: https://datatracker.ietf.org/doc/html/rfc1350.
559#[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        // NB: unicase::Ascii can't be used in match patterns
589        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
601/// The maximum number of options that a request may carry.
602pub 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/// TFTP Options.
610///
611/// TFTP options are introduced in [RFC 2347].
612///
613/// [RFC 2347]: https://datatracker.ietf.org/doc/html/rfc2347
614#[derive(Debug, Eq, PartialEq, Clone)]
615pub enum TftpOption {
616    /// Transfer size option, as defined in [RFC 2349].
617    ///
618    /// Encodes the size of the transfer, in bytes.
619    /// [RFC 2349]: https://datatracker.ietf.org/doc/html/rfc2349.
620    TransferSize(u64),
621    /// Block size option, as defined in [RFC 2348].
622    ///
623    /// The block size is the maximum  number of file bytes that can be
624    /// transferred in a single message.
625    ///
626    /// [RFC 2348]: https://datatracker.ietf.org/doc/html/rfc2348.
627    BlockSize(u16),
628    /// Timeout configuration option, as defined in [RFC 2349].
629    ///
630    /// Carries the negotiated timeout, in seconds.
631    ///
632    /// [RFC 2349]:https://datatracker.ietf.org/doc/html/rfc2349.
633    Timeout(u8),
634    /// Window size configuration option, as defined in [RFC 7440].
635    ///
636    /// The window size is the number of data blocks that can be transferred
637    /// between acknowledgements.
638    ///
639    /// [RFC 7440]: https://datatracker.ietf.org/doc/html/rfc7440.
640    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    /// Gets this options's serialized length.
689    ///
690    /// `forced` controls whether the option will be forced. Forceable options have
691    /// an appended `!` character which increases their length when serialized.
692    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 + both string lengths + 2 null termination characters
717        forced + option.len() + value_len + 2
718    }
719
720    /// Serializes the option into a buffer view.
721    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        // NB: We mask the opcode here because Fuchsia extensions use the rest
795        // of the opcode.
796        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/// The direction of a file transfer.
805#[derive(Debug, Copy, Clone)]
806pub enum TransferDirection {
807    Read,
808    Write,
809}
810
811#[derive(Debug)]
812/// Implements [`InnerPacketBuilder`] to build Read and Write requests.
813pub struct TransferRequestBuilder<'a> {
814    direction: TransferDirection,
815    filename: &'a str,
816    mode: TftpMode,
817    options: OptionCollection,
818}
819
820impl<'a> TransferRequestBuilder<'a> {
821    /// Creates a new builder with no options.
822    pub fn new(direction: TransferDirection, filename: &'a str, mode: TftpMode) -> Self {
823        Self { direction, filename, mode, options: OptionCollection::default() }
824    }
825
826    /// Creates a new builder with a set of options.
827    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/// Implements [`PacketBuilder`] for a data request.
864#[derive(Debug)]
865pub struct DataPacketBuilder {
866    block: u16,
867}
868
869impl DataPacketBuilder {
870    /// Creates a new builder.
871    pub fn new(block: u16) -> Self {
872        Self { block }
873    }
874}
875
876impl PacketBuilder 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    fn serialize(&self, target: &mut SerializeTarget<'_>, _body: FragmentedBytesMut<'_, '_>) {
887        let mut bv = crate::as_buffer_view_mut(&mut target.header);
888        bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::Data);
889        bv.take_obj_front::<U16>().unwrap().set(self.block);
890    }
891}
892
893/// Implements [`InnerPacketBuilder`] for ack messages.
894#[derive(Debug)]
895pub struct AckPacketBuilder {
896    block: u16,
897}
898
899impl AckPacketBuilder {
900    /// Creates a new builder.
901    pub fn new(block: u16) -> Self {
902        Self { block }
903    }
904}
905
906impl InnerPacketBuilder for AckPacketBuilder {
907    fn bytes_len(&self) -> usize {
908        std::mem::size_of::<MessageHead>() + std::mem::size_of::<U16>()
909    }
910
911    fn serialize(&self, mut buffer: &mut [u8]) {
912        let mut bv = crate::as_buffer_view_mut(&mut buffer);
913        bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::Ack);
914        bv.take_obj_front::<U16>().unwrap().set(self.block);
915    }
916}
917
918/// Implements [`InnerPacketBuilder`] for error messages.
919#[derive(Debug)]
920pub struct ErrorPacketBuilder<'a> {
921    error: TftpError,
922    msg: &'a str,
923}
924
925impl<'a> ErrorPacketBuilder<'a> {
926    /// Creates a new builder.
927    pub fn new(error: TftpError, msg: &'a str) -> Self {
928        Self { error, msg }
929    }
930}
931
932impl<'a> InnerPacketBuilder for ErrorPacketBuilder<'a> {
933    fn bytes_len(&self) -> usize {
934        std::mem::size_of::<MessageHead>()
935            + std::mem::size_of::<U16>()
936            + self.msg.as_bytes().len()
937            + 1
938    }
939
940    fn serialize(&self, mut buffer: &mut [u8]) {
941        let mut bv = crate::as_buffer_view_mut(&mut buffer);
942        bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::Error);
943        bv.take_obj_front::<U16>().unwrap().set(self.error.into());
944        write_str(&mut bv, self.msg);
945    }
946}
947
948/// Implements [`InnerPacketBuilder`] for option ack (OACK) messages.
949#[derive(Debug, Default)]
950pub struct OptionAckPacketBuilder {
951    options: OptionCollection,
952}
953
954impl OptionAckPacketBuilder {
955    /// Creates a new builder with the options in `options`.
956    pub fn new_with(options: impl IntoIterator<Item = Forceable<TftpOption>>) -> Self {
957        Self { options: options.into_iter().collect() }
958    }
959
960    pub fn options_mut(&mut self) -> &mut OptionCollection {
961        &mut self.options
962    }
963}
964
965impl InnerPacketBuilder for OptionAckPacketBuilder {
966    fn bytes_len(&self) -> usize {
967        std::mem::size_of::<MessageHead>() + self.options.serialized_len()
968    }
969
970    fn serialize(&self, mut buffer: &mut [u8]) {
971        let mut bv = crate::as_buffer_view_mut(&mut buffer);
972        bv.take_obj_front::<MessageHead>().unwrap().set_opcode(Opcode::OptionAck);
973        self.options.serialize(&mut bv);
974    }
975}
976
977#[cfg(test)]
978mod tests {
979    use super::*;
980    use packet::{ParseBuffer as _, Serializer as _};
981
982    const FILENAME: &'static str = "filename";
983
984    #[test]
985    fn test_read_request() {
986        let mut req =
987            TransferRequestBuilder::new(TransferDirection::Read, FILENAME, TftpMode::OCTET)
988                .into_serializer()
989                .serialize_vec_outer()
990                .unwrap_or_else(|_| panic!("failed to serialize"));
991        let body = match req.parse::<TftpPacket<_>>().expect("failed to parse") {
992            TftpPacket::ReadRequest(b) => b,
993            p => panic!("unexpected packet {:?}", p),
994        };
995        assert_eq!(body.filename(), FILENAME);
996        assert_eq!(body.mode(), TftpMode::OCTET);
997        assert!(body.options().iter().next().is_none());
998    }
999
1000    #[test]
1001    fn test_write_request() {
1002        let mut req =
1003            TransferRequestBuilder::new(TransferDirection::Write, FILENAME, TftpMode::OCTET)
1004                .into_serializer()
1005                .serialize_vec_outer()
1006                .unwrap_or_else(|_| panic!("failed to serialize"));
1007        let body = match req.parse::<TftpPacket<_>>().expect("failed to parse") {
1008            TftpPacket::WriteRequest(b) => b,
1009            p => panic!("unexpected packet {:?}", p),
1010        };
1011        assert_eq!(body.filename(), FILENAME);
1012        assert_eq!(body.mode(), TftpMode::OCTET);
1013        assert!(body.options().iter().next().is_none());
1014    }
1015
1016    #[test]
1017    fn test_data() {
1018        let data: Vec<_> = std::iter::successors(Some(0u8), |v| Some(*v + 1)).take(128).collect();
1019        let mut ser = DataPacketBuilder::new(123)
1020            .wrap_body((&data[..]).into_serializer())
1021            .serialize_vec_outer()
1022            .unwrap_or_else(|_| panic!("failed to serialize"));
1023        let body = match ser.parse::<TftpPacket<_>>().expect("failed to parse") {
1024            TftpPacket::Data(b) => b,
1025            p => panic!("unexpected packet {:?}", p),
1026        };
1027        assert_eq!(body.block(), 123);
1028        assert_eq!(body.payload().as_ref(), &data[..]);
1029    }
1030
1031    #[test]
1032    fn test_error() {
1033        const ERR_STR: &str = "ERROR";
1034        let mut err = ErrorPacketBuilder::new(TftpError::FileNotFound, ERR_STR)
1035            .into_serializer()
1036            .serialize_vec_outer()
1037            .unwrap_or_else(|_| panic!("failed to serialize"));
1038        let body = match err.parse::<TftpPacket<_>>().expect("failed to parse") {
1039            TftpPacket::Error(b) => b,
1040            p => panic!("unexpected packet {:?}", p),
1041        };
1042        assert_eq!(body.error(), TftpError::FileNotFound);
1043        assert_eq!(body.message(), ERR_STR);
1044    }
1045
1046    #[test]
1047    fn test_option_ack() {
1048        let builder = OptionAckPacketBuilder::new_with([
1049            TftpOption::WindowSize(10).not_forced(),
1050            TftpOption::BlockSize(35).not_forced(),
1051            TftpOption::Timeout(1).forced(),
1052            TftpOption::TransferSize(400).forced(),
1053        ]);
1054        let mut oack = builder
1055            .into_serializer()
1056            .serialize_vec_outer()
1057            .unwrap_or_else(|_| panic!("failed to serialize"));
1058        let body = match oack.parse::<TftpPacket<_>>().expect("failed to parse") {
1059            TftpPacket::OptionAck(b) => b,
1060            p => panic!("unexpected packet {:?}", p),
1061        };
1062        let AllOptions { window_size, block_size, timeout, transfer_size } =
1063            body.options().collect();
1064        assert_eq!(window_size, Some(Forceable { value: 10, forced: false }));
1065        assert_eq!(block_size, Some(Forceable { value: 35, forced: false }));
1066        assert_eq!(timeout, Some(Forceable { value: 1, forced: true }));
1067        assert_eq!(transfer_size, Some(Forceable { value: 400, forced: true }));
1068    }
1069
1070    #[test]
1071    fn test_ack() {
1072        let mut ack = AckPacketBuilder::new(123)
1073            .into_serializer()
1074            .serialize_vec_outer()
1075            .unwrap_or_else(|_| panic!("failed to serialize"));
1076        let body = match ack.parse::<TftpPacket<_>>().expect("failed to parse") {
1077            TftpPacket::Ack(b) => b,
1078            p => panic!("unexpected packet {:?}", p),
1079        };
1080        assert_eq!(body.block(), 123);
1081    }
1082
1083    #[test]
1084    fn test_transfer_request_options() {
1085        let builder = TransferRequestBuilder::new_with_options(
1086            TransferDirection::Read,
1087            FILENAME,
1088            TftpMode::OCTET,
1089            [
1090                TftpOption::WindowSize(10).not_forced(),
1091                TftpOption::BlockSize(35).not_forced(),
1092                TftpOption::Timeout(1).forced(),
1093                TftpOption::TransferSize(400).forced(),
1094            ],
1095        );
1096        let mut req = builder
1097            .into_serializer()
1098            .serialize_vec_outer()
1099            .unwrap_or_else(|_| panic!("failed to serialize"));
1100        let body = match req.parse::<TftpPacket<_>>().expect("failed to parse") {
1101            TftpPacket::ReadRequest(b) => b,
1102            p => panic!("unexpected packet {:?}", p),
1103        };
1104        let AllOptions { window_size, block_size, timeout, transfer_size } =
1105            body.options().collect();
1106        assert_eq!(window_size, Some(Forceable { value: 10, forced: false }));
1107        assert_eq!(block_size, Some(Forceable { value: 35, forced: false }));
1108        assert_eq!(timeout, Some(Forceable { value: 1, forced: true }));
1109        assert_eq!(transfer_size, Some(Forceable { value: 400, forced: true }));
1110    }
1111}