1 use core::borrow::{Borrow, BorrowMut};
2 
3 /// Options present in an [`crate::Ipv4Header`].
4 ///
5 /// IPv4 header options can only have a length that
6 /// is a multiple of 4 bytes (meaning 4, 8, 12, ...) and
7 /// a maximum length of 40 bytes (40 bytes the maximum length is
8 /// limited by maximum value of the "intra header length" field
9 /// in the IPv4 header).
10 ///
11 /// # Examples
12 ///
13 /// ```
14 /// use etherparse::Ipv4Options;
15 ///
16 /// {
17 ///     // static sized arrays of size 4,8,... 40 can directly be converted
18 ///     let options: Ipv4Options = [1,2,3,4].into();
19 ///     assert_eq!(&options[..], &[1,2,3,4]);
20 /// }
21 ///
22 /// {
23 ///     // slices can also be "try_from" converted
24 ///     let some_data = vec![1,2,3,4,5,6,7,8];
25 ///     let options: Ipv4Options = (&some_data[..]).try_into().unwrap();
26 ///     assert_eq!(options.as_slice(), &[1,2,3,4,5,6,7,8]);
27 /// }
28 /// {
29 ///     // only slices with a length that is multiple of 4 and a maximum value of 40
30 ///     // can be converted, otherwise you will get an error
31 ///     use etherparse::err::ipv4::BadOptionsLen;
32 ///
33 ///     let result = Ipv4Options::try_from(&[1,2,3][..]);
34 ///     assert_eq!(result, Err(BadOptionsLen { bad_len: 3 }));
35 /// }
36 /// ```
37 #[derive(Clone)]
38 pub struct Ipv4Options {
39     pub(crate) len: u8,
40     pub(crate) buf: [u8; 40],
41 }
42 
43 impl Ipv4Options {
44     /// Maximum length of the IPv4 options in bytes.
45     pub const MAX_LEN: u8 = 40;
46 
47     /// Setup an empty options array.
48     #[inline]
new() -> Ipv4Options49     pub fn new() -> Ipv4Options {
50         Ipv4Options {
51             len: 0,
52             buf: [0; 40],
53         }
54     }
55 
56     /// Returns the slice containing the data of the options.
57     #[inline]
as_slice(&self) -> &[u8]58     pub fn as_slice(&self) -> &[u8] {
59         unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len.into()) }
60     }
61 
62     /// Returns a mutable slice containing the data of the options.
63     #[inline]
as_mut_slice(&mut self) -> &mut [u8]64     pub fn as_mut_slice(&mut self) -> &mut [u8] {
65         unsafe { core::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.len.into()) }
66     }
67 
68     /// Returns the length of the options in bytes.
69     #[inline]
len(&self) -> usize70     pub fn len(&self) -> usize {
71         usize::from(self.len)
72     }
73 
74     /// Returns the length of the options in bytes.
75     #[inline]
len_u8(&self) -> u876     pub fn len_u8(&self) -> u8 {
77         self.len
78     }
79 
80     /// Returns if the length of the options is zero.
81     #[inline]
is_empty(&self) -> bool82     pub fn is_empty(&self) -> bool {
83         self.len == 0
84     }
85 }
86 
87 impl TryFrom<&[u8]> for Ipv4Options {
88     type Error = crate::err::ipv4::BadOptionsLen;
89 
try_from(value: &[u8]) -> Result<Self, Self::Error>90     fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
91         if value.len() <= 40 && value.len() % 4 == 0 {
92             let mut result = Ipv4Options {
93                 len: value.len() as u8,
94                 buf: [0; 40],
95             };
96             unsafe {
97                 // SAFETY: Safe as value.len() <= 40 and the result buffer size is 40.
98                 core::ptr::copy_nonoverlapping(
99                     value.as_ptr(),
100                     result.buf.as_mut_ptr(),
101                     value.len(),
102                 );
103             }
104             Ok(result)
105         } else {
106             Err(Self::Error {
107                 bad_len: value.len(),
108             })
109         }
110     }
111 }
112 
113 impl Default for Ipv4Options {
114     #[inline]
default() -> Self115     fn default() -> Self {
116         Self {
117             len: 0,
118             buf: [0; 40],
119         }
120     }
121 }
122 
123 impl core::fmt::Debug for Ipv4Options {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result124     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
125         self.as_slice().fmt(f)
126     }
127 }
128 
129 impl PartialEq for Ipv4Options {
eq(&self, other: &Self) -> bool130     fn eq(&self, other: &Self) -> bool {
131         self.as_slice() == other.as_slice()
132     }
133 }
134 impl Eq for Ipv4Options {}
135 
136 impl core::hash::Hash for Ipv4Options {
hash<H: core::hash::Hasher>(&self, state: &mut H)137     fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
138         self.as_slice().hash(state);
139     }
140 }
141 
142 impl core::cmp::PartialOrd for Ipv4Options {
partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering>143     fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
144         Some(self.as_slice().cmp(other.as_slice()))
145     }
146 }
147 
148 impl core::cmp::Ord for Ipv4Options {
cmp(&self, other: &Self) -> core::cmp::Ordering149     fn cmp(&self, other: &Self) -> core::cmp::Ordering {
150         self.as_slice().cmp(other.as_slice())
151     }
152 }
153 
154 impl From<[u8; 0]> for Ipv4Options {
155     #[inline]
from(_: [u8; 0]) -> Self156     fn from(_: [u8; 0]) -> Self {
157         Ipv4Options {
158             len: 0,
159             buf: [0; 40],
160         }
161     }
162 }
163 
164 macro_rules! from_static_array {
165     ($x:expr) => {
166         impl From<[u8; $x]> for Ipv4Options {
167             #[inline]
168             fn from(values: [u8; $x]) -> Self {
169                 let mut result = Ipv4Options {
170                     len: $x,
171                     buf: [0; 40],
172                 };
173                 let r = result.buf.as_mut_ptr() as *mut [u8; $x];
174                 unsafe {
175                     *r = values;
176                 }
177                 result
178             }
179         }
180     };
181 }
182 
183 from_static_array!(4);
184 from_static_array!(8);
185 from_static_array!(12);
186 from_static_array!(16);
187 from_static_array!(20);
188 from_static_array!(24);
189 from_static_array!(28);
190 from_static_array!(32);
191 from_static_array!(36);
192 
193 impl From<[u8; 40]> for Ipv4Options {
from(values: [u8; 40]) -> Self194     fn from(values: [u8; 40]) -> Self {
195         Ipv4Options {
196             len: 40,
197             buf: values,
198         }
199     }
200 }
201 
202 impl AsRef<Ipv4Options> for Ipv4Options {
as_ref(&self) -> &Ipv4Options203     fn as_ref(&self) -> &Ipv4Options {
204         self
205     }
206 }
207 
208 impl AsRef<[u8]> for Ipv4Options {
as_ref(&self) -> &[u8]209     fn as_ref(&self) -> &[u8] {
210         self.as_slice()
211     }
212 }
213 
214 impl AsMut<Ipv4Options> for Ipv4Options {
as_mut(&mut self) -> &mut Ipv4Options215     fn as_mut(&mut self) -> &mut Ipv4Options {
216         self
217     }
218 }
219 
220 impl AsMut<[u8]> for Ipv4Options {
as_mut(&mut self) -> &mut [u8]221     fn as_mut(&mut self) -> &mut [u8] {
222         self.as_mut_slice()
223     }
224 }
225 
226 impl Borrow<[u8]> for Ipv4Options {
borrow(&self) -> &[u8]227     fn borrow(&self) -> &[u8] {
228         self.as_slice()
229     }
230 }
231 
232 impl BorrowMut<[u8]> for Ipv4Options {
borrow_mut(&mut self) -> &mut [u8]233     fn borrow_mut(&mut self) -> &mut [u8] {
234         self.as_mut_slice()
235     }
236 }
237 
238 impl core::ops::Deref for Ipv4Options {
239     type Target = [u8];
240 
241     #[inline]
deref(&self) -> &[u8]242     fn deref(&self) -> &[u8] {
243         self.as_slice()
244     }
245 }
246 
247 impl core::ops::DerefMut for Ipv4Options {
248     #[inline]
deref_mut(&mut self) -> &mut [u8]249     fn deref_mut(&mut self) -> &mut [u8] {
250         self.as_mut_slice()
251     }
252 }
253 
254 #[cfg(test)]
255 mod tests {
256     use super::*;
257     use crate::test_gens::*;
258     use proptest::prelude::*;
259     use std::format;
260 
261     #[test]
new()262     fn new() {
263         let actual = Ipv4Options::new();
264         assert_eq!(actual.len, 0);
265         assert_eq!(actual.buf, [0; 40]);
266     }
267 
268     #[test]
is_empty()269     fn is_empty() {
270         {
271             let actual = Ipv4Options::new();
272             assert!(actual.is_empty());
273         }
274         {
275             let actual: Ipv4Options = [1, 2, 3, 4].into();
276             assert_eq!(false, actual.is_empty());
277         }
278     }
279 
280     #[test]
try_from()281     fn try_from() {
282         const DATA: [u8; 48] = [
283             1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
284             25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
285             47, 48,
286         ];
287 
288         // ok cases
289         for len_div_4 in 0usize..=10 {
290             let mut actual = Ipv4Options::try_from(&DATA[..len_div_4 * 4]).unwrap();
291             assert_eq!(actual.as_slice(), &DATA[..len_div_4 * 4]);
292             assert_eq!(actual.as_mut_slice(), &DATA[..len_div_4 * 4]);
293             assert_eq!(actual.len_u8(), (len_div_4 * 4) as u8);
294             assert_eq!(actual.len(), len_div_4 * 4);
295         }
296 
297         // error cases
298         use crate::err::ipv4::BadOptionsLen;
299         for len in 0usize..48 {
300             if (len % 4 != 0) || len > 40 {
301                 assert_eq!(
302                     Err(BadOptionsLen { bad_len: len }),
303                     Ipv4Options::try_from(&DATA[..len])
304                 )
305             }
306         }
307     }
308 
309     #[test]
default()310     fn default() {
311         let actual: Ipv4Options = Default::default();
312         assert_eq!(actual.len, 0);
313         assert_eq!(actual.buf, [0; 40]);
314     }
315 
316     proptest! {
317         #[test]
318         fn clone_dbg(options in ipv4_options_any()) {
319             assert_eq!(
320                 format!("{:?}", options),
321                 format!("{:?}", options.clone().as_slice())
322             );
323         }
324     }
325 
326     proptest! {
327         #[test]
328         fn eq_partial_eq(
329             a in ipv4_options_any(),
330             b in ipv4_options_any()
331         ) {
332             assert_eq!(a.eq(&b), a.as_slice().eq(b.as_slice()));
333             assert_eq!(a == b, a.as_slice() == b.as_slice());
334         }
335     }
336 
337     proptest! {
338         #[test]
339         fn hash(
340             options in ipv4_options_any()
341         ) {
342             use std::collections::hash_map::DefaultHasher;
343             use core::hash::{Hash, Hasher};
344             let a = {
345                 let mut hasher = DefaultHasher::new();
346                 options.hash(&mut hasher);
347                 hasher.finish()
348             };
349             let b = {
350                 let mut hasher = DefaultHasher::new();
351                 options.hash(&mut hasher);
352                 hasher.finish()
353             };
354             assert_eq!(a, b);
355         }
356     }
357 
358     proptest! {
359         #[test]
360         fn ord_partial_ord(
361             a in ipv4_options_any(),
362             b in ipv4_options_any()
363         ) {
364             assert_eq!(a.cmp(&b), a.as_slice().cmp(&b.as_slice()));
365             assert_eq!(a.partial_cmp(&b), a.as_slice().partial_cmp(&b.as_slice()));
366         }
367     }
368 
369     #[test]
from_0_byte_array()370     fn from_0_byte_array() {
371         let options: Ipv4Options = [].into();
372         assert_eq!(&options[..], &[]);
373     }
374 
375     macro_rules! from_static_array_test {
376         ($func_name:ident, $x:expr) => {
377             #[test]
378             fn $func_name() {
379                 {
380                     let options: Ipv4Options = [$x; $x].into();
381                     assert_eq!(&options[..], &[$x; $x]);
382                 }
383                 assert_eq!(&Ipv4Options::from([$x; $x])[..], &[$x; $x]);
384             }
385         };
386     }
387 
388     from_static_array_test!(from_arr_4, 4);
389     from_static_array_test!(from_arr_8, 8);
390     from_static_array_test!(from_arr_12, 12);
391     from_static_array_test!(from_arr_16, 16);
392     from_static_array_test!(from_arr_20, 20);
393     from_static_array_test!(from_arr_24, 24);
394     from_static_array_test!(from_arr_28, 28);
395     from_static_array_test!(from_arr_32, 32);
396     from_static_array_test!(from_arr_36, 36);
397     from_static_array_test!(from_arr_40, 40);
398 
399     proptest! {
400         #[test]
401         fn as_ref(options in ipv4_options_any()) {
402             // as object reference
403             {
404                 let r: &Ipv4Options = options.as_ref();
405                 assert_eq!(r, &options);
406             }
407             // as slice reference
408             {
409                 let r: &[u8] = options.as_ref();
410                 assert_eq!(r, options.as_slice());
411             }
412         }
413     }
414 
415     proptest! {
416         #[test]
417         fn as_mut(options in ipv4_options_any()) {
418             // as object reference
419             {
420                 let mut o = options.clone();
421                 let r: &mut Ipv4Options = o.as_mut();
422                 if r.len() > 0 {
423                     r[0] = 123;
424                     assert_eq!(123, o.as_slice()[0]);
425                 }
426             }
427             // as slice reference
428             {
429                 let mut o = options.clone();
430                 let r: &mut [u8] = o.as_mut();
431                 if r.len() > 0 {
432                     r[0] = 123;
433                     assert_eq!(123, o.as_slice()[0]);
434                 }
435             }
436         }
437     }
438 
439     proptest! {
440         #[test]
441         fn borrow(options in ipv4_options_any()) {
442             // as object reference
443             {
444                 let r: &Ipv4Options = options.borrow();
445                 assert_eq!(r, &options);
446             }
447             // as slice reference
448             {
449                 let r: &[u8] = options.borrow();
450                 assert_eq!(r, options.as_slice());
451             }
452         }
453     }
454 
455     proptest! {
456         #[test]
457         fn borrow_mut(options in ipv4_options_any()) {
458             // as object reference
459             {
460                 let mut o = options.clone();
461                 let r: &mut Ipv4Options = o.borrow_mut();
462                 if r.len() > 0 {
463                     r[0] = 123;
464                     assert_eq!(123, o.as_slice()[0]);
465                 }
466             }
467             // as slice reference
468             {
469                 let mut o = options.clone();
470                 let r: &mut [u8] = o.borrow_mut();
471                 if r.len() > 0 {
472                     r[0] = 123;
473                     assert_eq!(123, o.as_slice()[0]);
474                 }
475             }
476         }
477     }
478 
479     #[test]
deref()480     fn deref() {
481         let options: Ipv4Options = [1, 2, 3, 4].into();
482         let s: &[u8] = &options;
483         assert_eq!(s, &[1, 2, 3, 4]);
484         assert_eq!(&options[..], &[1, 2, 3, 4]);
485     }
486 
487     #[test]
deref_mut()488     fn deref_mut() {
489         let mut options: Ipv4Options = [1, 2, 3, 4].into();
490         let s: &mut [u8] = &mut options;
491         assert_eq!(s, &[1, 2, 3, 4]);
492     }
493 }
494