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 = &[0; 239];
216        let result = MessageSlicer::new(input);
217        assert!(result.is_err());
218    }
219
220    #[test]
221    fn test_message_slicer_with_large_input() {
222        init_logger();
223        let input = &[0; 240];
224        let result = MessageSlicer::new(input);
225        assert!(result.is_ok());
226    }
227}