async_tftp/
packet.rs

1//! Packet definitions.
2use bytes::{BufMut, Bytes, BytesMut};
3use std::convert::From;
4use std::io;
5use std::str;
6
7use crate::error::Result;
8use crate::parse::*;
9
10pub(crate) const PACKET_DATA_HEADER_LEN: usize = 4;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(u16)]
14pub(crate) enum PacketType {
15    Rrq = 1,
16    Wrq = 2,
17    Data = 3,
18    Ack = 4,
19    Error = 5,
20    OAck = 6,
21}
22
23/// TFTP protocol error. Should not be confused with `async_tftp::Error`.
24#[derive(Debug, Clone)]
25pub enum Error {
26    Msg(String),
27    UnknownError,
28    FileNotFound,
29    PermissionDenied,
30    DiskFull,
31    IllegalOperation,
32    UnknownTransferId,
33    FileAlreadyExists,
34    NoSuchUser,
35}
36
37#[derive(Debug)]
38pub(crate) enum Packet<'a> {
39    Rrq(RwReq),
40    Wrq(RwReq),
41    Data(u16, &'a [u8]),
42    Ack(u16),
43    Error(Error),
44    OAck(Opts),
45}
46
47#[derive(Debug, PartialEq)]
48pub(crate) enum Mode {
49    Netascii,
50    Octet,
51    Mail,
52}
53
54#[derive(Debug, PartialEq)]
55pub(crate) struct RwReq {
56    pub filename: String,
57    pub mode: Mode,
58    pub opts: Opts,
59}
60
61#[derive(Debug, Clone, Default, PartialEq)]
62pub(crate) struct Opts {
63    pub block_size: Option<u16>,
64    pub timeout: Option<u8>,
65    pub transfer_size: Option<u64>,
66    pub window_size: Option<u16>,
67}
68
69impl PacketType {
70    pub(crate) fn from_u16(n: u16) -> Option<PacketType> {
71        match n {
72            1 => Some(PacketType::Rrq),
73            2 => Some(PacketType::Wrq),
74            3 => Some(PacketType::Data),
75            4 => Some(PacketType::Ack),
76            5 => Some(PacketType::Error),
77            6 => Some(PacketType::OAck),
78            _ => None,
79        }
80    }
81}
82
83impl From<PacketType> for u16 {
84    fn from(value: PacketType) -> Self {
85        value as u16
86    }
87}
88
89impl<'a> Packet<'a> {
90    pub(crate) fn decode(data: &[u8]) -> Result<Packet<'_>> {
91        parse_packet(data)
92    }
93
94    pub(crate) fn encode(&self, buf: &mut BytesMut) {
95        match self {
96            Packet::Rrq(req) => {
97                buf.put_u16(PacketType::Rrq.into());
98                buf.put_slice(req.filename.as_bytes());
99                buf.put_u8(0);
100                buf.put_slice(req.mode.to_str().as_bytes());
101                buf.put_u8(0);
102                req.opts.encode(buf);
103            }
104            Packet::Wrq(req) => {
105                buf.put_u16(PacketType::Wrq.into());
106                buf.put_slice(req.filename.as_bytes());
107                buf.put_u8(0);
108                buf.put_slice(req.mode.to_str().as_bytes());
109                buf.put_u8(0);
110                req.opts.encode(buf);
111            }
112            Packet::Data(block, data) => {
113                buf.put_u16(PacketType::Data.into());
114                buf.put_u16(*block);
115                buf.put_slice(data);
116            }
117            Packet::Ack(block) => {
118                buf.put_u16(PacketType::Ack.into());
119                buf.put_u16(*block);
120            }
121            Packet::Error(error) => {
122                buf.put_u16(PacketType::Error.into());
123                buf.put_u16(error.code());
124                buf.put_slice(error.msg().as_bytes());
125                buf.put_u8(0);
126            }
127            Packet::OAck(opts) => {
128                buf.put_u16(PacketType::OAck.into());
129                opts.encode(buf);
130            }
131        }
132    }
133
134    pub(crate) fn encode_data_head(block_id: u16, buf: &mut BytesMut) {
135        buf.put_u16(PacketType::Data.into());
136        buf.put_u16(block_id);
137    }
138
139    pub(crate) fn to_bytes(&self) -> Bytes {
140        let mut buf = BytesMut::new();
141        self.encode(&mut buf);
142        buf.freeze()
143    }
144}
145
146impl Opts {
147    fn encode(&self, buf: &mut BytesMut) {
148        if let Some(block_size) = self.block_size {
149            buf.put_slice(&b"blksize\0"[..]);
150            buf.put_slice(block_size.to_string().as_bytes());
151            buf.put_u8(0);
152        }
153
154        if let Some(timeout) = self.timeout {
155            buf.put_slice(&b"timeout\0"[..]);
156            buf.put_slice(timeout.to_string().as_bytes());
157            buf.put_u8(0);
158        }
159
160        if let Some(transfer_size) = self.transfer_size {
161            buf.put_slice(&b"tsize\0"[..]);
162            buf.put_slice(transfer_size.to_string().as_bytes());
163            buf.put_u8(0);
164        }
165
166        if let Some(window_size) = self.window_size {
167            buf.put_slice(&b"windowsize\0"[..]);
168            buf.put_slice(window_size.to_string().as_bytes());
169            buf.put_u8(0);
170        }
171    }
172}
173
174impl Mode {
175    pub(crate) fn to_str(&self) -> &'static str {
176        match self {
177            Mode::Netascii => "netascii",
178            Mode::Octet => "octet",
179            Mode::Mail => "mail",
180        }
181    }
182}
183
184impl Error {
185    pub(crate) fn from_code(code: u16, msg: Option<&str>) -> Self {
186        #[allow(clippy::wildcard_in_or_patterns)]
187        match code {
188            1 => Error::FileNotFound,
189            2 => Error::PermissionDenied,
190            3 => Error::DiskFull,
191            4 => Error::IllegalOperation,
192            5 => Error::UnknownTransferId,
193            6 => Error::FileAlreadyExists,
194            7 => Error::NoSuchUser,
195            0 | _ => match msg {
196                Some(msg) => Error::Msg(msg.to_string()),
197                None => Error::UnknownError,
198            },
199        }
200    }
201
202    pub(crate) fn code(&self) -> u16 {
203        match self {
204            Error::Msg(..) => 0,
205            Error::UnknownError => 0,
206            Error::FileNotFound => 1,
207            Error::PermissionDenied => 2,
208            Error::DiskFull => 3,
209            Error::IllegalOperation => 4,
210            Error::UnknownTransferId => 5,
211            Error::FileAlreadyExists => 6,
212            Error::NoSuchUser => 7,
213        }
214    }
215
216    pub(crate) fn msg(&self) -> &str {
217        match self {
218            Error::Msg(msg) => msg,
219            Error::UnknownError => "Unknown error",
220            Error::FileNotFound => "File not found",
221            Error::PermissionDenied => "Permission denied",
222            Error::DiskFull => "Disk is full",
223            Error::IllegalOperation => "Illegal operation",
224            Error::UnknownTransferId => "Unknown transfer ID",
225            Error::FileAlreadyExists => "File already exists",
226            Error::NoSuchUser => "No such user",
227        }
228    }
229}
230
231impl From<Error> for Packet<'_> {
232    fn from(inner: Error) -> Self {
233        Packet::Error(inner)
234    }
235}
236
237impl From<io::Error> for Error {
238    fn from(io_err: io::Error) -> Self {
239        match io_err.kind() {
240            io::ErrorKind::NotFound => Error::FileNotFound,
241            io::ErrorKind::PermissionDenied => Error::PermissionDenied,
242            io::ErrorKind::WriteZero => Error::DiskFull,
243            io::ErrorKind::AlreadyExists => Error::FileAlreadyExists,
244            _ => match io_err.raw_os_error() {
245                Some(rc) => Error::Msg(format!("IO error: {}", rc)),
246                None => Error::UnknownError,
247            },
248        }
249    }
250}
251
252impl From<crate::Error> for Error {
253    fn from(err: crate::Error) -> Self {
254        match err {
255            crate::Error::Packet(e) => e,
256            crate::Error::Io(e) => e.into(),
257            crate::Error::InvalidPacket => Error::IllegalOperation,
258            crate::Error::MaxSendRetriesReached(..) => {
259                Error::Msg("Max retries reached".to_string())
260            }
261            _ => Error::UnknownError,
262        }
263    }
264}