Thanks to visit codestin.com
Credit goes to docs.rs

etherparse/transport/
udp_header.rs

1use crate::{err::ValueTooBigError, *};
2
3/// Udp header according to rfc768.
4#[derive(Clone, Debug, Eq, PartialEq, Default)]
5pub 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
16impl 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
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
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.
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.
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.
108    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
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.
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.
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
182    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.
200    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]
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]
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]
232    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")))]
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")))]
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]
268    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]
277    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]
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)]
303mod 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.
360    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.
594    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]
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}