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