toe_beans/v4/message/
slicer.rs

1use std::cell::RefCell;
2
3use crate::v4::error::Result;
4use log::trace;
5
6/// Takes a slice as input and divides it into smaller slices.
7/// For example `&[1, 2, 3, 4] --> &[1, 2] and &[3, 4]`.
8///
9/// Uses:
10/// - Used by `MessageSlicer` to help those implementing `DecodeMessage`'s `from_bytes`.
11/// - When parsing formats within formats. For example,
12///   the DHCP format has an options field that contains TLV
13///   formatted options.
14pub struct Slicer<'de> {
15    input: &'de [u8],
16    input_index: usize,
17}
18
19impl<'de> Slicer<'de> {
20    /// Creates an instance of Slicer.
21    pub fn new(input: &'de [u8]) -> Self {
22        Slicer {
23            input,
24            input_index: 0,
25        }
26    }
27
28    /// Returns the next len number of bytes and changes the current position the same amount.
29    /// Can panic if parsing past array length. Use `parse` for no panic.
30    #[inline]
31    fn parse_unchecked(&mut self, len: usize) -> &'de [u8] {
32        trace!("parse_unchecked");
33        let slice = &self.input[self.input_index..self.input_index + len];
34        self.input_index += len;
35        slice
36    }
37
38    /// Returns the remaining bytes in the input and sets the current position to the end.
39    #[inline]
40    fn parse_remainder(&mut self) -> &'de [u8] {
41        trace!("parse_remainder");
42        let slice = &self.input[self.input_index..];
43        self.input_index += slice.len();
44        slice
45    }
46
47    /// Same as `parse_unchecked`, but returns Option instead of a panic.
48    #[inline]
49    pub fn parse(&mut self, len: usize) -> Option<&'de [u8]> {
50        trace!("parse");
51        let slice = self.input.get(self.input_index..self.input_index + len);
52
53        if slice.is_some() {
54            self.input_index += len;
55        }
56
57        slice
58    }
59}
60
61/// MessageSlicer wraps a Slicer to provide:
62/// 1. Methods to parse fields because we already know the fixed length of those fields.
63/// 2. A validated input. We know a Message must be a minimum length because we know the size of certain fields. This allows us to use faster unchecked parsing if we check the length at instantiation.
64///
65/// MessageSlicer is useful to implement `from_bytes` from `DecodeMessage`.
66pub struct MessageSlicer<'de> {
67    slicer: RefCell<Slicer<'de>>,
68}
69
70impl<'de> MessageSlicer<'de> {
71    /// Creates a MessageSlicer for use in DecodeMessage
72    #[inline]
73    pub fn new(input: &'de [u8]) -> Result<Self> {
74        if input.len() < 240 {
75            return Err("Can't decode input less than 240 bytes");
76        }
77
78        Ok(Self {
79            slicer: RefCell::new(Slicer {
80                input,
81                input_index: 0,
82            }),
83        })
84    }
85
86    /// Parses 1 byte to be used for the op field
87    #[inline]
88    pub fn parse_op(&self) -> &'de [u8] {
89        trace!("parse_op");
90        self.slicer.borrow_mut().parse_unchecked(1)
91    }
92
93    /// Parses 1 byte to be used for the htype field
94    #[inline]
95    pub fn parse_htype(&self) -> &'de [u8] {
96        trace!("parse_htypes");
97        self.slicer.borrow_mut().parse_unchecked(1)
98    }
99
100    /// Parses 1 byte to be used for the hlen field
101    #[inline]
102    pub fn parse_hlen(&self) -> &'de [u8] {
103        trace!("parse_hlen");
104        self.slicer.borrow_mut().parse_unchecked(1)
105    }
106
107    /// Parses 1 byte to be used for the hops field
108    #[inline]
109    pub fn parse_hops(&self) -> &'de [u8] {
110        trace!("parse_hops");
111        self.slicer.borrow_mut().parse_unchecked(1)
112    }
113
114    /// Parses 4 bytes to be used for the xid field
115    #[inline]
116    pub fn parse_xid(&self) -> &'de [u8] {
117        trace!("parse_xid");
118        self.slicer.borrow_mut().parse_unchecked(4)
119    }
120
121    /// Parses 2 bytes to be used for the secs field
122    #[inline]
123    pub fn parse_secs(&self) -> &'de [u8] {
124        trace!("parse_secs");
125        self.slicer.borrow_mut().parse_unchecked(2)
126    }
127
128    /// Parses 2 bytes to be used for the flags field
129    #[inline]
130    pub fn parse_flags(&self) -> &'de [u8] {
131        trace!("parse_flags");
132        self.slicer.borrow_mut().parse_unchecked(2)
133    }
134
135    /// Parses 4 bytes to be used for the ciaddr field
136    #[inline]
137    pub fn parse_ciaddr(&self) -> &'de [u8] {
138        trace!("parse_ciaddr");
139        self.slicer.borrow_mut().parse_unchecked(4)
140    }
141
142    /// Parses 4 bytes to be used for the yiaddr field
143    #[inline]
144    pub fn parse_yiaddr(&self) -> &'de [u8] {
145        trace!("parse_yiaddr");
146        self.slicer.borrow_mut().parse_unchecked(4)
147    }
148
149    /// Parses 4 bytes to be used for the siaddr field
150    #[inline]
151    pub fn parse_siaddr(&self) -> &'de [u8] {
152        trace!("parse_siaddr");
153        self.slicer.borrow_mut().parse_unchecked(4)
154    }
155
156    /// Parses 4 bytes to be used for the giaddr field
157    #[inline]
158    pub fn parse_giaddr(&self) -> &'de [u8] {
159        trace!("parse_giaddr");
160        self.slicer.borrow_mut().parse_unchecked(4)
161    }
162
163    /// Parses 16 bytes to be used for the chaddr field
164    #[inline]
165    pub fn parse_chaddr(&self) -> &'de [u8] {
166        trace!("parse_chaddr");
167        self.slicer.borrow_mut().parse_unchecked(16)
168    }
169
170    /// Parses 64 bytes to be used for the sname field
171    #[inline]
172    pub fn parse_sname(&self) -> &'de [u8] {
173        trace!("parse_sname");
174        self.slicer.borrow_mut().parse_unchecked(64)
175    }
176
177    /// Parses 128 bytes to be used for the file field
178    #[inline]
179    pub fn parse_file(&self) -> &'de [u8] {
180        trace!("parse_file");
181        self.slicer.borrow_mut().parse_unchecked(128)
182    }
183
184    /// Parses 4 bytes to be used as the magic field
185    #[inline]
186    pub fn parse_magic(&self) -> &'de [u8] {
187        trace!("parse_magic");
188        self.slicer.borrow_mut().parse_unchecked(4)
189    }
190
191    /// Parses all remaining bytes in the input
192    /// to be used as the options field
193    #[inline]
194    pub fn parse_options(&self) -> &'de [u8] {
195        trace!("parse_options");
196        self.slicer.borrow_mut().parse_remainder()
197    }
198}
199
200// ------------------------------------------------
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    // Output additional debug info with
207    // RUST_LOG=debug cargo test test_from_bytes -- --show-output
208    fn init_logger() {
209        let _ = env_logger::builder().is_test(true).try_init();
210    }
211
212    #[test]
213    fn test_message_slicer_with_small_input() {
214        init_logger();
215        let input = &[
216            // length 239
217            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
218            9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
219            8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6,
220            7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
221            6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4,
222            5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
223            4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2,
224            3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
225            2, 3, 4, 5, 6, 7, 8,
226        ];
227        let result = MessageSlicer::new(input);
228        assert!(result.is_err());
229    }
230
231    #[test]
232    fn test_message_slicer_with_large_input() {
233        init_logger();
234        let input = &[
235            // length 240
236            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
237            9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
238            8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6,
239            7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
240            6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4,
241            5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
242            4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2,
243            3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
244            2, 3, 4, 5, 6, 7, 8, 9,
245        ];
246        let result = MessageSlicer::new(input);
247        assert!(result.is_ok());
248    }
249}