use std::convert::TryFrom;
use crate::{
checks::transport::udp::{UDP_HEADER_SIZE, validate_udp_length, validate_udp_min_length},
errors::transport::udp::UdpError,
};
#[cfg_attr(doc, aquamarine::aquamarine)]
#[derive(Debug)]
pub struct UdpPacket<'a> {
pub source_port: u16,
pub destination_port: u16,
pub length: u16,
pub checksum: u16,
pub payload: &'a [u8],
}
impl<'a> TryFrom<&'a [u8]> for UdpPacket<'a> {
type Error = UdpError;
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
validate_udp_min_length(data)?;
let source_port = u16::from_be_bytes([data[0], data[1]]);
let destination_port = u16::from_be_bytes([data[2], data[3]]);
let length = u16::from_be_bytes([data[4], data[5]]);
let checksum = u16::from_be_bytes([data[6], data[7]]);
validate_udp_length(length, data.len())?;
let payload = &data[UDP_HEADER_SIZE..];
Ok(UdpPacket {
source_port,
destination_port,
length,
checksum,
payload,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_udp_packet_parsing() {
let data = [
0x04, 0xD2, 0x00, 0x50, 0x00, 0x14, 0x12, 0x34, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, ];
let udp_packet = UdpPacket::try_from(&data[..]).unwrap();
assert_eq!(udp_packet.source_port, 1234);
assert_eq!(udp_packet.destination_port, 80);
assert_eq!(udp_packet.length, 20);
assert_eq!(udp_packet.checksum, 0x1234);
assert_eq!(
udp_packet.payload,
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C
]
);
}
#[test]
fn test_udp_packet_too_short() {
let data = [0x04, 0xD2, 0x00, 0x50]; let result = UdpPacket::try_from(&data[..]);
assert!(matches!(
result,
Err(UdpError::PacketTooShort {
expected: 8,
actual: 4
})
));
}
#[test]
fn test_udp_packet_invalid_length() {
let data = [
0x04, 0xD2, 0x00, 0x50, 0x00, 0x10, 0x12, 0x34, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
];
let result = UdpPacket::try_from(&data[..]);
assert!(matches!(
result,
Err(UdpError::InvalidLength {
length: 16,
actual: 18
})
));
}
}