1 use crate::{err::ValueTooBigError, *};
2 
3 /// Udp header according to rfc768.
4 #[derive(Clone, Debug, Eq, PartialEq, Default)]
5 pub struct UdpHeader {
6     /// Source port of the packet (optional).
7     pub source_port: u16,
8     /// Destination port of the packet.
9     pub destination_port: u16,
10     /// Length of the packet (includes the udp header length of 8 bytes).
11     pub length: u16,
12     /// The checksum of the packet. The checksum is calculated from a pseudo header, the udp header and the payload. The pseudo header is composed of source and destination address, protocol number
13     pub checksum: u16,
14 }
15 
16 impl UdpHeader {
17     /// Serialized size of an UDP header in bytes/octets.
18     pub const LEN: usize = 8;
19 
20     /// Serialized size of an UDP header in bytes/octets in an [`u16`].
21     pub const LEN_U16: u16 = 8;
22 
23     #[deprecated(since = "0.14.0", note = "Use `UdpHeader::LEN` instead")]
24     pub const SERIALIZED_SIZE: usize = UdpHeader::LEN;
25 
26     /// Returns an udp header for the given parameters
without_ipv4_checksum( source_port: u16, destination_port: u16, payload_length: usize, ) -> Result<UdpHeader, ValueTooBigError<usize>>27     pub fn without_ipv4_checksum(
28         source_port: u16,
29         destination_port: u16,
30         payload_length: usize,
31     ) -> Result<UdpHeader, ValueTooBigError<usize>> {
32         // check that the total length fits into the field
33         const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
34         if MAX_PAYLOAD_LENGTH < payload_length {
35             return Err(ValueTooBigError {
36                 actual: payload_length,
37                 max_allowed: MAX_PAYLOAD_LENGTH,
38                 value_type: err::ValueType::UdpPayloadLengthIpv4,
39             });
40         }
41 
42         Ok(UdpHeader {
43             source_port,
44             destination_port,
45             length: (UdpHeader::LEN + payload_length) as u16, //payload plus udp header
46             checksum: 0,
47         })
48     }
49 
50     /// Calculate an udp header given an ipv4 header and the payload
with_ipv4_checksum( source_port: u16, destination_port: u16, ip_header: &Ipv4Header, payload: &[u8], ) -> Result<UdpHeader, ValueTooBigError<usize>>51     pub fn with_ipv4_checksum(
52         source_port: u16,
53         destination_port: u16,
54         ip_header: &Ipv4Header,
55         payload: &[u8],
56     ) -> Result<UdpHeader, ValueTooBigError<usize>> {
57         // check that the total length fits into the field
58         const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
59         if MAX_PAYLOAD_LENGTH < payload.len() {
60             return Err(ValueTooBigError {
61                 actual: payload.len(),
62                 max_allowed: MAX_PAYLOAD_LENGTH,
63                 value_type: err::ValueType::UdpPayloadLengthIpv4,
64             });
65         }
66 
67         let mut result = UdpHeader {
68             source_port,
69             destination_port,
70             length: (UdpHeader::LEN + payload.len()) as u16, //payload plus udp header
71             checksum: 0,
72         };
73         result.checksum =
74             result.calc_checksum_ipv4_internal(ip_header.source, ip_header.destination, payload);
75         Ok(result)
76     }
77 
78     /// Calculates the upd header checksum based on a ipv4 header.
calc_checksum_ipv4( &self, ip_header: &Ipv4Header, payload: &[u8], ) -> Result<u16, ValueTooBigError<usize>>79     pub fn calc_checksum_ipv4(
80         &self,
81         ip_header: &Ipv4Header,
82         payload: &[u8],
83     ) -> Result<u16, ValueTooBigError<usize>> {
84         self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload)
85     }
86 
87     /// Calculates the upd header checksum based on a ipv4 header.
calc_checksum_ipv4_raw( &self, source: [u8; 4], destination: [u8; 4], payload: &[u8], ) -> Result<u16, ValueTooBigError<usize>>88     pub fn calc_checksum_ipv4_raw(
89         &self,
90         source: [u8; 4],
91         destination: [u8; 4],
92         payload: &[u8],
93     ) -> Result<u16, ValueTooBigError<usize>> {
94         // check that the total length fits into the field
95         const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
96         if MAX_PAYLOAD_LENGTH < payload.len() {
97             return Err(ValueTooBigError {
98                 actual: payload.len(),
99                 max_allowed: MAX_PAYLOAD_LENGTH,
100                 value_type: err::ValueType::UdpPayloadLengthIpv4,
101             });
102         }
103 
104         Ok(self.calc_checksum_ipv4_internal(source, destination, payload))
105     }
106 
107     /// Calculates the upd header checksum based on a ipv4 header.
calc_checksum_ipv4_internal( &self, source: [u8; 4], destination: [u8; 4], payload: &[u8], ) -> u16108     fn calc_checksum_ipv4_internal(
109         &self,
110         source: [u8; 4],
111         destination: [u8; 4],
112         payload: &[u8],
113     ) -> u16 {
114         self.calc_checksum_post_ip(
115             //pseudo header
116             checksum::Sum16BitWords::new()
117                 .add_4bytes(source)
118                 .add_4bytes(destination)
119                 .add_2bytes([0, ip_number::UDP.0])
120                 .add_2bytes(self.length.to_be_bytes()),
121             payload,
122         )
123     }
124 
125     /// Calculate an udp header given an ipv6 header and the payload
with_ipv6_checksum( source_port: u16, destination_port: u16, ip_header: &Ipv6Header, payload: &[u8], ) -> Result<UdpHeader, ValueTooBigError<usize>>126     pub fn with_ipv6_checksum(
127         source_port: u16,
128         destination_port: u16,
129         ip_header: &Ipv6Header,
130         payload: &[u8],
131     ) -> Result<UdpHeader, ValueTooBigError<usize>> {
132         // check that the total length fits into the field
133         const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
134         if MAX_PAYLOAD_LENGTH < payload.len() {
135             return Err(ValueTooBigError {
136                 actual: payload.len(),
137                 max_allowed: MAX_PAYLOAD_LENGTH,
138                 value_type: err::ValueType::UdpPayloadLengthIpv6,
139             });
140         }
141 
142         let mut result = UdpHeader {
143             source_port,
144             destination_port,
145             length: (UdpHeader::LEN + payload.len()) as u16, //payload plus udp header
146             checksum: 0,
147         };
148         result.checksum =
149             result.calc_checksum_ipv6_internal(ip_header.source, ip_header.destination, payload);
150         Ok(result)
151     }
152 
153     /// Calculates the checksum of the current udp header given an ipv6 header and the payload.
calc_checksum_ipv6( &self, ip_header: &Ipv6Header, payload: &[u8], ) -> Result<u16, err::ValueTooBigError<usize>>154     pub fn calc_checksum_ipv6(
155         &self,
156         ip_header: &Ipv6Header,
157         payload: &[u8],
158     ) -> Result<u16, err::ValueTooBigError<usize>> {
159         self.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, payload)
160     }
161 
162     /// Calculates the checksum of the current udp header given an ipv6 source & destination address plus the payload.
calc_checksum_ipv6_raw( &self, source: [u8; 16], destination: [u8; 16], payload: &[u8], ) -> Result<u16, err::ValueTooBigError<usize>>163     pub fn calc_checksum_ipv6_raw(
164         &self,
165         source: [u8; 16],
166         destination: [u8; 16],
167         payload: &[u8],
168     ) -> Result<u16, err::ValueTooBigError<usize>> {
169         //check that the total length fits into the field
170         const MAX_PAYLOAD_LENGTH: usize = (u32::MAX as usize) - UdpHeader::LEN;
171         if MAX_PAYLOAD_LENGTH < payload.len() {
172             return Err(err::ValueTooBigError {
173                 actual: payload.len(),
174                 max_allowed: MAX_PAYLOAD_LENGTH,
175                 value_type: err::ValueType::UdpPayloadLengthIpv6,
176             });
177         }
178 
179         Ok(self.calc_checksum_ipv6_internal(source, destination, payload))
180     }
181 
calc_checksum_ipv6_internal( &self, source: [u8; 16], destination: [u8; 16], payload: &[u8], ) -> u16182     fn calc_checksum_ipv6_internal(
183         &self,
184         source: [u8; 16],
185         destination: [u8; 16],
186         payload: &[u8],
187     ) -> u16 {
188         self.calc_checksum_post_ip(
189             //pseudo header
190             checksum::Sum16BitWords::new()
191                 .add_16bytes(source)
192                 .add_16bytes(destination)
193                 .add_2bytes([0, ip_number::UDP.0])
194                 .add_2bytes(self.length.to_be_bytes()),
195             payload,
196         )
197     }
198 
199     /// This method takes the sum of the pseudo ip header and calculates the rest of the checksum.
calc_checksum_post_ip( &self, ip_pseudo_header_sum: checksum::Sum16BitWords, payload: &[u8], ) -> u16200     fn calc_checksum_post_ip(
201         &self,
202         ip_pseudo_header_sum: checksum::Sum16BitWords,
203         payload: &[u8],
204     ) -> u16 {
205         ip_pseudo_header_sum
206             .add_2bytes(self.source_port.to_be_bytes())
207             .add_2bytes(self.destination_port.to_be_bytes())
208             .add_2bytes(self.length.to_be_bytes())
209             .add_slice(payload)
210             .to_ones_complement_with_no_zero()
211             .to_be()
212     }
213 
214     /// Reads a udp header from a slice directly and returns a tuple containing the resulting header & unused part of the slice.
215     #[deprecated(since = "0.10.1", note = "Use UdpHeader::from_slice instead.")]
216     #[inline]
read_from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), err::LenError>217     pub fn read_from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), err::LenError> {
218         UdpHeader::from_slice(slice)
219     }
220 
221     /// Reads a udp header from a slice directly and returns a tuple containing the resulting header & unused part of the slice.
222     #[inline]
from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), err::LenError>223     pub fn from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), err::LenError> {
224         Ok((
225             UdpHeaderSlice::from_slice(slice)?.to_header(),
226             &slice[UdpHeader::LEN..],
227         ))
228     }
229 
230     /// Read an UdpHeader from a static sized byte array.
231     #[inline]
from_bytes(bytes: [u8; 8]) -> UdpHeader232     pub fn from_bytes(bytes: [u8; 8]) -> UdpHeader {
233         UdpHeader {
234             source_port: u16::from_be_bytes([bytes[0], bytes[1]]),
235             destination_port: u16::from_be_bytes([bytes[2], bytes[3]]),
236             length: u16::from_be_bytes([bytes[4], bytes[5]]),
237             checksum: u16::from_be_bytes([bytes[6], bytes[7]]),
238         }
239     }
240 
241     /// Tries to read an udp header from the current position.
242     #[cfg(feature = "std")]
243     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
read<T: std::io::Read + std::io::Seek + Sized>( reader: &mut T, ) -> Result<UdpHeader, std::io::Error>244     pub fn read<T: std::io::Read + std::io::Seek + Sized>(
245         reader: &mut T,
246     ) -> Result<UdpHeader, std::io::Error> {
247         let bytes = {
248             let mut bytes: [u8; 8] = [0; 8];
249             reader.read_exact(&mut bytes)?;
250             bytes
251         };
252         Ok(UdpHeader::from_bytes(bytes))
253     }
254 
255     /// Write the udp header without recalculating the checksum or length.
256     #[cfg(feature = "std")]
257     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error>258     pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
259         writer.write_all(&self.to_bytes())?;
260         Ok(())
261     }
262 
263     /// Length of the serialized header in bytes.
264     ///
265     /// The function always returns the constant [`crate::UdpHeader::LEN`]
266     /// and exists to keep the methods consistent with other headers.
267     #[inline]
header_len(&self) -> usize268     pub const fn header_len(&self) -> usize {
269         UdpHeader::LEN
270     }
271 
272     /// Length of the serialized header in bytes in an [`u16`].
273     ///
274     /// The function always returns the constant [`crate::UdpHeader::LEN_U16`]
275     /// and exists to keep the methods consistent with other headers.
276     #[inline]
header_len_u16(&self) -> u16277     pub const fn header_len_u16(&self) -> u16 {
278         UdpHeader::LEN_U16
279     }
280 
281     /// Returns the serialized form of the header as a statically
282     /// sized byte array.
283     #[inline]
to_bytes(&self) -> [u8; 8]284     pub fn to_bytes(&self) -> [u8; 8] {
285         let source_port_be = self.source_port.to_be_bytes();
286         let destination_port_be = self.destination_port.to_be_bytes();
287         let length_be = self.length.to_be_bytes();
288         let checksum = self.checksum.to_be_bytes();
289         [
290             source_port_be[0],
291             source_port_be[1],
292             destination_port_be[0],
293             destination_port_be[1],
294             length_be[0],
295             length_be[1],
296             checksum[0],
297             checksum[1],
298         ]
299     }
300 }
301 
302 #[cfg(test)]
303 mod test {
304     use crate::{
305         err::{ValueTooBigError, ValueType},
306         test_gens::*,
307         *,
308     };
309     use alloc::{format, vec::Vec};
310     use proptest::prelude::*;
311     use std::io::Cursor;
312 
313     proptest! {
314         #[test]
315         fn without_ipv4_checksum(
316             source_port in any::<u16>(),
317             destination_port in any::<u16>(),
318             good_payload_length in 0..=((core::u16::MAX as usize) - UdpHeader::LEN),
319             bad_payload_length in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
320         ) {
321 
322             // normal working call
323             {
324                 let actual = UdpHeader::without_ipv4_checksum(
325                     source_port,
326                     destination_port,
327                     good_payload_length
328                 ).unwrap();
329                 assert_eq!(
330                     actual,
331                     UdpHeader{
332                         source_port,
333                         destination_port,
334                         length: (UdpHeader::LEN + good_payload_length) as u16,
335                         checksum: 0
336                     }
337                 );
338             }
339 
340             // length too large
341             {
342                 let actual = UdpHeader::without_ipv4_checksum(
343                     source_port,
344                     destination_port,
345                     bad_payload_length
346                 ).unwrap_err();
347                 assert_eq!(
348                     actual,
349                     ValueTooBigError{
350                         actual: bad_payload_length,
351                         max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
352                         value_type: err::ValueType::UdpPayloadLengthIpv4,
353                     }
354                 );
355             }
356         }
357     }
358 
359     /// Calculat the expected UDP header checksum for the tests.
expected_udp_ipv4_checksum( source: [u8; 4], destination: [u8; 4], udp_header: &UdpHeader, payload: &[u8], ) -> u16360     fn expected_udp_ipv4_checksum(
361         source: [u8; 4],
362         destination: [u8; 4],
363         udp_header: &UdpHeader,
364         payload: &[u8],
365     ) -> u16 {
366         checksum::Sum16BitWords::new()
367             // pseudo header
368             .add_4bytes(source)
369             .add_4bytes(destination)
370             .add_2bytes([0, ip_number::UDP.0])
371             .add_2bytes(udp_header.length.to_be_bytes())
372             // udp header
373             .add_2bytes(udp_header.source_port.to_be_bytes())
374             .add_2bytes(udp_header.destination_port.to_be_bytes())
375             .add_2bytes(udp_header.length.to_be_bytes())
376             .add_2bytes([0, 0]) // checksum as zero (should have no effect)
377             .add_slice(payload)
378             .to_ones_complement_with_no_zero()
379             .to_be()
380     }
381 
382     proptest! {
383         #[test]
384         fn with_ipv4_checksum(
385             source_port in any::<u16>(),
386             destination_port in any::<u16>(),
387             ipv4 in ipv4_any(),
388             payload in proptest::collection::vec(any::<u8>(), 0..20),
389             bad_len in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
390         ) {
391             // normal case
392             assert_eq!(
393                 UdpHeader::with_ipv4_checksum(
394                     source_port,
395                     destination_port,
396                     &ipv4,
397                     &payload
398                 ).unwrap(),
399                 {
400                     let mut expected = UdpHeader {
401                         source_port,
402                         destination_port,
403                         length: (UdpHeader::LEN + payload.len()) as u16,
404                         checksum: 0,
405                     };
406                     let checksum = expected_udp_ipv4_checksum(
407                         ipv4.source,
408                         ipv4.destination,
409                         &expected,
410                         &payload
411                     );
412                     expected.checksum = checksum;
413                     expected
414                 }
415             );
416 
417             // case where the 16 bit word results in a checksum of
418             // 0, but gets converted to 0xffff as 0 is reserved.
419             {
420                 let base = UdpHeader {
421                     source_port: 0,
422                     destination_port,
423                     length: (UdpHeader::LEN + payload.len()) as u16,
424                     checksum: 0,
425                 };
426                 // use the source port to force 0 as a result value
427                 // for that first calculate the checksum with the source
428                 // set to 0
429                 let sourceless_checksum = !(expected_udp_ipv4_checksum(
430                     ipv4.source,
431                     ipv4.destination,
432                     &base,
433                     &payload
434                 ).to_le());
435 
436                 assert_eq!(
437                     UdpHeader::with_ipv4_checksum(
438                         // we now need to add a value that results in the value
439                         // 0xffff (which will become 0 via the ones complement rule).
440                         0xffff - sourceless_checksum,
441                         destination_port,
442                         &ipv4,
443                         &payload
444                     ).unwrap(),
445                     UdpHeader{
446                         source_port: 0xffff - sourceless_checksum,
447                         destination_port,
448                         length: base.length,
449                         checksum: 0xffff
450                     }
451                 );
452             }
453 
454             // length error case
455             {
456                 // SAFETY: In case the error is not triggered
457                 //         a segmentation fault will be triggered.
458                 let too_big_slice = unsafe {
459                     //NOTE: The pointer must be initialized with a non null value
460                     //      otherwise a key constraint of slices is not fulfilled
461                     //      which can lead to crashes in release mode.
462                     use core::ptr::NonNull;
463                     core::slice::from_raw_parts(
464                         NonNull::<u8>::dangling().as_ptr(),
465                         bad_len
466                     )
467                 };
468                 assert_eq!(
469                     UdpHeader::with_ipv4_checksum(
470                         source_port,
471                         destination_port,
472                         &ipv4,
473                         &too_big_slice
474                     ).unwrap_err(),
475                     ValueTooBigError{
476                         actual: bad_len,
477                         max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
478                         value_type: err::ValueType::UdpPayloadLengthIpv4,
479                     }
480                 );
481             }
482         }
483     }
484 
485     proptest! {
486         #[test]
487         fn calc_checksum_ipv4_raw(
488             source_port in any::<u16>(),
489             destination_port in any::<u16>(),
490             dummy_checksum in any::<u16>(),
491             ipv4 in ipv4_any(),
492             payload in proptest::collection::vec(any::<u8>(), 0..20),
493             bad_len in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
494         ) {
495             // normal case
496             {
497                 let header = UdpHeader {
498                     source_port,
499                     destination_port,
500                     length: (UdpHeader::LEN + payload.len()) as u16,
501                     checksum: dummy_checksum,
502                 };
503 
504                 assert_eq!(
505                     header.calc_checksum_ipv4_raw(
506                         ipv4.source,
507                         ipv4.destination,
508                         &payload
509                     ).unwrap(),
510                     expected_udp_ipv4_checksum(
511                         ipv4.source,
512                         ipv4.destination,
513                         &header,
514                         &payload
515                     )
516                 );
517             }
518 
519             // case where the 16 bit word results in a checksum of
520             // 0, but gets converted to 0xffff as 0 is reserved.
521             {
522                 let base = UdpHeader {
523                     source_port: 0,
524                     destination_port,
525                     length: (UdpHeader::LEN + payload.len()) as u16,
526                     checksum: dummy_checksum,
527                 };
528                 // use the source port to force 0 as a result value
529                 // for that first calculate the checksum with the source
530                 // set to 0
531                 let sourceless_checksum = !(expected_udp_ipv4_checksum(
532                     ipv4.source,
533                     ipv4.destination,
534                     &base,
535                     &payload
536                 ).to_le());
537 
538                 // we now need to add a value that results in the value
539                 // 0xffff (which will become 0 via the ones complement rule).
540                 let header = {
541                     let mut header = base.clone();
542                     header.source_port = 0xffff - sourceless_checksum;
543                     header
544                 };
545 
546                 assert_eq!(
547                     0xffff,
548                     header.calc_checksum_ipv4_raw(
549                         ipv4.source,
550                         ipv4.destination,
551                         &payload
552                     ).unwrap()
553                 );
554             }
555 
556             // length error case
557             {
558                 let header = UdpHeader {
559                     source_port,
560                     destination_port,
561                     // udp header length itself is ok, but the payload not
562                     length: (UdpHeader::LEN + payload.len()) as u16,
563                     checksum: dummy_checksum,
564                 };
565                 // SAFETY: In case the error is not triggered
566                 //         a segmentation fault will be triggered.
567                 let too_big_slice = unsafe {
568                     //NOTE: The pointer must be initialized with a non null value
569                     //      otherwise a key constraint of slices is not fulfilled
570                     //      which can lead to crashes in release mode.
571                     use core::ptr::NonNull;
572                     core::slice::from_raw_parts(
573                         NonNull::<u8>::dangling().as_ptr(),
574                         bad_len
575                     )
576                 };
577                 assert_eq!(
578                     header.calc_checksum_ipv4_raw(
579                         ipv4.source,
580                         ipv4.destination,
581                         too_big_slice
582                     ).unwrap_err(),
583                     ValueTooBigError{
584                         actual: bad_len,
585                         max_allowed: (core::u16::MAX as usize) - UdpHeader::LEN,
586                         value_type: ValueType::UdpPayloadLengthIpv4,
587                     }
588                 );
589             }
590         }
591     }
592 
593     /// Calculat the expected UDP header checksum for the tests.
expected_udp_ipv6_checksum( source: [u8; 16], destination: [u8; 16], udp_header: &UdpHeader, payload: &[u8], ) -> u16594     fn expected_udp_ipv6_checksum(
595         source: [u8; 16],
596         destination: [u8; 16],
597         udp_header: &UdpHeader,
598         payload: &[u8],
599     ) -> u16 {
600         checksum::Sum16BitWords::new()
601             // pseudo header
602             .add_16bytes(source)
603             .add_16bytes(destination)
604             .add_2bytes([0, ip_number::UDP.0])
605             .add_4bytes(u32::from(udp_header.length).to_be_bytes())
606             // udp header
607             .add_2bytes(udp_header.source_port.to_be_bytes())
608             .add_2bytes(udp_header.destination_port.to_be_bytes())
609             .add_2bytes(udp_header.length.to_be_bytes())
610             .add_2bytes([0, 0]) // checksum as zero (should have no effect)
611             .add_slice(payload)
612             .to_ones_complement_with_no_zero()
613             .to_be()
614     }
615 
616     proptest! {
617         #[test]
618         fn with_ipv6_checksum(
619             source_port in any::<u16>(),
620             destination_port in any::<u16>(),
621             ipv6 in ipv6_any(),
622             payload in proptest::collection::vec(any::<u8>(), 0..20),
623             bad_len in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
624         ) {
625             // normal case
626             assert_eq!(
627                 UdpHeader::with_ipv6_checksum(
628                     source_port,
629                     destination_port,
630                     &ipv6,
631                     &payload
632                 ).unwrap(),
633                 {
634                     let mut expected = UdpHeader {
635                         source_port,
636                         destination_port,
637                         length: (UdpHeader::LEN + payload.len()) as u16,
638                         checksum: 0,
639                     };
640                     let checksum = expected_udp_ipv6_checksum(
641                         ipv6.source,
642                         ipv6.destination,
643                         &expected,
644                         &payload
645                     );
646                     expected.checksum = checksum;
647                     expected
648                 }
649             );
650 
651             // case where the 16 bit word results in a checksum of
652             // 0, but gets converted to 0xffff as 0 is reserved.
653             {
654                 let base = UdpHeader {
655                     source_port: 0,
656                     destination_port,
657                     length: (UdpHeader::LEN + payload.len()) as u16,
658                     checksum: 0,
659                 };
660                 // use the source port to force 0 as a result value
661                 // for that first calculate the checksum with the source
662                 // set to 0
663                 let sourceless_checksum = !(expected_udp_ipv6_checksum(
664                     ipv6.source,
665                     ipv6.destination,
666                     &base,
667                     &payload
668                 ).to_le());
669 
670                 assert_eq!(
671                     UdpHeader::with_ipv6_checksum(
672                         // we now need to add a value that results in the value
673                         // 0xffff (which will become 0 via the ones complement rule).
674                         0xffff - sourceless_checksum,
675                         destination_port,
676                         &ipv6,
677                         &payload
678                     ).unwrap(),
679                     UdpHeader{
680                         source_port: 0xffff - sourceless_checksum,
681                         destination_port,
682                         length: base.length,
683                         checksum: 0xffff
684                     }
685                 );
686             }
687 
688             // length error case
689             {
690                 // SAFETY: In case the error is not triggered
691                 //         a segmentation fault will be triggered.
692                 let too_big_slice = unsafe {
693                     //NOTE: The pointer must be initialized with a non null value
694                     //      otherwise a key constraint of slices is not fulfilled
695                     //      which can lead to crashes in release mode.
696                     use core::ptr::NonNull;
697                     core::slice::from_raw_parts(
698                         NonNull::<u8>::dangling().as_ptr(),
699                         bad_len
700                     )
701                 };
702                 assert_eq!(
703                     UdpHeader::with_ipv6_checksum(
704                         source_port,
705                         destination_port,
706                         &ipv6,
707                         &too_big_slice
708                     ).unwrap_err(),
709                     ValueTooBigError{
710                         actual: bad_len,
711                         max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
712                         value_type: err::ValueType::UdpPayloadLengthIpv6,
713                     }
714                 );
715             }
716         }
717     }
718 
719     proptest! {
720         #[test]
721         #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
722         fn calc_checksum_ipv6(
723             source_port in any::<u16>(),
724             destination_port in any::<u16>(),
725             ipv6 in ipv6_any(),
726             payload in proptest::collection::vec(any::<u8>(), 0..20),
727             bad_len in ((core::u32::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
728         ) {
729             // normal case
730             assert_eq!(
731                 UdpHeader::with_ipv6_checksum(
732                     source_port,
733                     destination_port,
734                     &ipv6,
735                     &payload
736                 ).unwrap(),
737                 {
738                     let mut expected = UdpHeader {
739                         source_port,
740                         destination_port,
741                         length: (UdpHeader::LEN + payload.len()) as u16,
742                         checksum: 0,
743                     };
744                     let checksum = expected_udp_ipv6_checksum(
745                         ipv6.source,
746                         ipv6.destination,
747                         &expected,
748                         &payload
749                     );
750                     expected.checksum = checksum;
751                     expected
752                 }
753             );
754 
755             // case where the 16 bit word results in a checksum of
756             // 0, but gets converted to 0xffff as 0 is reserved.
757             {
758                 let base = UdpHeader {
759                     source_port: 0,
760                     destination_port,
761                     length: (UdpHeader::LEN + payload.len()) as u16,
762                     checksum: 0,
763                 };
764                 // use the source port to force 0 as a result value
765                 // for that first calculate the checksum with the source
766                 // set to 0
767                 let sourceless_checksum = !(expected_udp_ipv6_checksum(
768                     ipv6.source,
769                     ipv6.destination,
770                     &base,
771                     &payload
772                 ).to_le());
773 
774                 assert_eq!(
775                     UdpHeader::with_ipv6_checksum(
776                         // we now need to add a value that results in the value
777                         // 0xffff (which will become 0 via the ones complement rule).
778                         0xffff - sourceless_checksum,
779                         destination_port,
780                         &ipv6,
781                         &payload
782                     ).unwrap(),
783                     UdpHeader{
784                         source_port: 0xffff - sourceless_checksum,
785                         destination_port,
786                         length: base.length,
787                         checksum: 0xffff
788                     }
789                 );
790             }
791 
792             // length error case
793             {
794                 // SAFETY: In case the error is not triggered
795                 //         a segmentation fault will be triggered.
796                 let too_big_slice = unsafe {
797                     //NOTE: The pointer must be initialized with a non null value
798                     //      otherwise a key constraint of slices is not fulfilled
799                     //      which can lead to crashes in release mode.
800                     use core::ptr::NonNull;
801                     core::slice::from_raw_parts(
802                         NonNull::<u8>::dangling().as_ptr(),
803                         bad_len
804                     )
805                 };
806                 assert_eq!(
807                     UdpHeader::with_ipv6_checksum(
808                         source_port,
809                         destination_port,
810                         &ipv6,
811                         &too_big_slice
812                     ).unwrap_err(),
813                     ValueTooBigError{
814                         actual: bad_len,
815                         max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
816                         value_type: err::ValueType::UdpPayloadLengthIpv6,
817                     }
818                 );
819             }
820         }
821     }
822 
823     proptest! {
824         #[test]
825         #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
826         fn calc_checksum_ipv6_raw(
827             source_port in any::<u16>(),
828             destination_port in any::<u16>(),
829             dummy_checksum in any::<u16>(),
830             ipv6 in ipv6_any(),
831             payload in proptest::collection::vec(any::<u8>(), 0..20),
832             bad_len in ((core::u32::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
833         ) {
834             // normal case
835             {
836                 let header = UdpHeader {
837                     source_port,
838                     destination_port,
839                     length: (UdpHeader::LEN + payload.len()) as u16,
840                     checksum: dummy_checksum,
841                 };
842 
843                 assert_eq!(
844                     header.calc_checksum_ipv6_raw(
845                         ipv6.source,
846                         ipv6.destination,
847                         &payload
848                     ).unwrap(),
849                     expected_udp_ipv6_checksum(
850                         ipv6.source,
851                         ipv6.destination,
852                         &header,
853                         &payload
854                     )
855                 );
856             }
857 
858             // case where the 16 bit word results in a checksum of
859             // 0, but gets converted to 0xffff as 0 is reserved.
860             {
861                 let base = UdpHeader {
862                     source_port: 0,
863                     destination_port,
864                     length: (UdpHeader::LEN + payload.len()) as u16,
865                     checksum: dummy_checksum,
866                 };
867                 // use the source port to force 0 as a result value
868                 // for that first calculate the checksum with the source
869                 // set to 0
870                 let sourceless_checksum = !(expected_udp_ipv6_checksum(
871                     ipv6.source,
872                     ipv6.destination,
873                     &base,
874                     &payload
875                 ).to_le());
876 
877                 // we now need to add a value that results in the value
878                 // 0xffff (which will become 0 via the ones complement rule).
879                 let header = {
880                     let mut header = base.clone();
881                     header.source_port = 0xffff - sourceless_checksum;
882                     header
883                 };
884 
885                 assert_eq!(
886                     0xffff,
887                     header.calc_checksum_ipv6_raw(
888                         ipv6.source,
889                         ipv6.destination,
890                         &payload
891                     ).unwrap()
892                 );
893             }
894 
895             // length error case
896             {
897                 let header = UdpHeader {
898                     source_port,
899                     destination_port,
900                     // udp header length itself is ok, but the payload not
901                     length: (UdpHeader::LEN + payload.len()) as u16,
902                     checksum: dummy_checksum,
903                 };
904                 // SAFETY: In case the error is not triggered
905                 //         a segmentation fault will be triggered.
906                 let too_big_slice = unsafe {
907                     //NOTE: The pointer must be initialized with a non null value
908                     //      otherwise a key constraint of slices is not fulfilled
909                     //      which can lead to crashes in release mode.
910                     use core::ptr::NonNull;
911                     core::slice::from_raw_parts(
912                         NonNull::<u8>::dangling().as_ptr(),
913                         bad_len
914                     )
915                 };
916                 assert_eq!(
917                     header.calc_checksum_ipv6_raw(
918                         ipv6.source,
919                         ipv6.destination,
920                         too_big_slice
921                     ).unwrap_err(),
922                     ValueTooBigError{
923                         actual: bad_len,
924                         max_allowed: (core::u32::MAX as usize) - UdpHeader::LEN,
925                         value_type: ValueType::UdpPayloadLengthIpv6,
926                     }
927                 );
928             }
929         }
930     }
931 
932     proptest! {
933         #[test]
934         fn from_slice(
935             input in udp_any(),
936             dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
937         ) {
938             // serialize
939             let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
940             input.write(&mut buffer).unwrap();
941             buffer.extend(&dummy_data[..]);
942 
943             // calls with a valid result
944             {
945                 let (result, rest) = UdpHeader::from_slice(&buffer[..]).unwrap();
946                 assert_eq!(result, input);
947                 assert_eq!(rest, &buffer[8..]);
948             }
949             #[allow(deprecated)]
950             {
951                 let (result, rest) = UdpHeader::read_from_slice(&buffer[..]).unwrap();
952                 assert_eq!(result, input);
953                 assert_eq!(rest, &buffer[8..]);
954             }
955 
956             // call with not enough data in the slice
957             for len in 0..8 {
958                 assert_eq!(
959                     UdpHeader::from_slice(&buffer[0..len]).unwrap_err(),
960                     err::LenError{
961                         required_len: UdpHeader::LEN,
962                         len: len,
963                         len_source: LenSource::Slice,
964                         layer: err::Layer::UdpHeader,
965                         layer_start_offset: 0,
966                     }
967                 );
968             }
969         }
970     }
971 
972     proptest! {
973         #[test]
974         fn from_bytes(input in udp_any()) {
975             assert_eq!(
976                 input,
977                 UdpHeader::from_bytes(
978                     input.to_bytes()
979                 )
980             );
981         }
982     }
983 
984     proptest! {
985         #[test]
986         fn read(
987             input in udp_any(),
988             dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
989         ) {
990             // serialize
991             let mut buffer: Vec<u8> = Vec::with_capacity(input.header_len() + dummy_data.len());
992             input.write(&mut buffer).unwrap();
993             buffer.extend(&dummy_data[..]);
994 
995             // normal
996             {
997                 let mut cursor = Cursor::new(&buffer);
998                 let result = UdpHeader::read(&mut cursor).unwrap();
999                 assert_eq!(result, input);
1000                 assert_eq!(8, cursor.position());
1001             }
1002 
1003             // unexpexted eof
1004             for len in 0..8 {
1005                 let mut cursor = Cursor::new(&buffer[0..len]);
1006                 assert!(
1007                     UdpHeader::read(&mut cursor)
1008                     .is_err()
1009                 );
1010             }
1011         }
1012     }
1013 
1014     proptest! {
1015         #[test]
1016         fn write(input in udp_any()) {
1017             // normal write
1018             {
1019                 let mut result = Vec::with_capacity(input.header_len());
1020                 input.write(&mut result).unwrap();
1021                 assert_eq!(
1022                     &result[..],
1023                     input.to_bytes()
1024                 );
1025             }
1026 
1027             // unexpected eof
1028             for len in 0..8 {
1029                 let mut buffer = [0u8; 8];
1030                 let mut cursor = Cursor::new(&mut buffer[..len]);
1031                 assert!(
1032                     input.write(&mut cursor)
1033                         .is_err()
1034                 );
1035             }
1036         }
1037     }
1038 
1039     proptest! {
1040         #[test]
1041         fn to_bytes(input in udp_any()) {
1042             let s_be = input.source_port.to_be_bytes();
1043             let d_be = input.destination_port.to_be_bytes();
1044             let l_be = input.length.to_be_bytes();
1045             let c_be = input.checksum.to_be_bytes();
1046 
1047             assert_eq!(
1048                 input.to_bytes(),
1049                 [
1050                     s_be[0],
1051                     s_be[1],
1052                     d_be[0],
1053                     d_be[1],
1054                     l_be[0],
1055                     l_be[1],
1056                     c_be[0],
1057                     c_be[1],
1058                 ]
1059             );
1060         }
1061     }
1062 
1063     #[test]
default()1064     fn default() {
1065         let actual: UdpHeader = Default::default();
1066         assert_eq!(actual.source_port, 0);
1067         assert_eq!(actual.destination_port, 0);
1068         assert_eq!(actual.length, 0);
1069         assert_eq!(actual.checksum, 0);
1070     }
1071 
1072     proptest! {
1073         #[test]
1074         fn clone_eq(input in udp_any()) {
1075             assert_eq!(input, input.clone());
1076             {
1077                 let mut other = input.clone();
1078                 other.source_port = !input.source_port;
1079                 assert!(input != other);
1080             }
1081         }
1082     }
1083 
1084     proptest! {
1085         #[test]
1086         fn dbg(input in udp_any()) {
1087             assert_eq!(
1088                 &format!(
1089                     "UdpHeader {{ source_port: {}, destination_port: {}, length: {}, checksum: {} }}",
1090                     input.source_port,
1091                     input.destination_port,
1092                     input.length,
1093                     input.checksum,
1094                 ),
1095                 &format!("{:?}", input)
1096             );
1097         }
1098     }
1099 }
1100