async_tftp/
packet.rs

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