internet 0.1.0

Network library for rust
Documentation
//! UDP Header Mapping.
//!
//! [RFC 768]: https://datatracker.ietf.org/doc/html/rfc768

use crate::ietf::udp::{Checksum, Port};
use crate::{Buf, BufError, BufMut, BufResult};

/// UDP header mapping following [RFC 768].
///
/// [RFC 768]: https://datatracker.ietf.org/doc/html/rfc768
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HeaderMapping<T: Buf> {
    buffer: T,
}

impl<T: Buf> HeaderMapping<T> {
    /// Creates a new header mapping.
    pub fn new(buffer: T) -> Self {
        Self { buffer }
    }

    /// Consumes the mapping and returns the underlying buffer.
    pub fn into_inner(self) -> T {
        self.buffer
    }

    /// Returns a reference to the underlying buffer.
    pub fn as_inner(&self) -> &T {
        &self.buffer
    }
}

impl<T: Buf> HeaderMapping<T> {
    /// Reads the source port.
    pub fn read_source_port(&self) -> Port {
        unsafe { Port::from(self.buffer.get_u16_be_unchecked(0)) }
    }

    /// Reads the destination port.
    pub fn read_destination_port(&self) -> Port {
        unsafe { Port::from(self.buffer.get_u16_be_unchecked(2)) }
    }

    /// Reads the length.
    pub fn read_length(&self) -> u16 {
        unsafe { self.buffer.get_u16_be_unchecked(4) }
    }

    /// Reads the checksum.
    pub fn read_checksum(&self) -> Checksum {
        unsafe { Checksum(self.buffer.get_u16_be_unchecked(6)) }
    }
}

impl<T: BufMut> HeaderMapping<T> {
    /// Writes the source port.
    pub fn write_source_port(&mut self, port: Port) -> BufResult<()> {
        if self.buffer.length() < 2 {
            return Err(BufError::BufferTooSmall);
        }
        unsafe { self.buffer.set_u16_be_unchecked(0, port.into()) }
        Ok(())
    }

    /// Writes the destination port.
    pub fn write_destination_port(&mut self, port: Port) -> BufResult<()> {
        if self.buffer.length() < 4 {
            return Err(BufError::BufferTooSmall);
        }
        unsafe { self.buffer.set_u16_be_unchecked(2, port.into()) }
        Ok(())
    }

    /// Writes the length field.
    pub fn write_length(&mut self, length: u16) -> BufResult<()> {
        if self.buffer.length() < 6 {
            return Err(BufError::BufferTooSmall);
        }
        unsafe { self.buffer.set_u16_be_unchecked(4, length) }
        Ok(())
    }

    /// Writes the checksum.
    pub fn write_checksum(&mut self, checksum: Checksum) -> BufResult<()> {
        if self.buffer.length() < 8 {
            return Err(BufError::BufferTooSmall);
        }
        unsafe { self.buffer.set_u16_be_unchecked(6, checksum.0) }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::{Checksum, HeaderMapping, Port};

    #[test]
    fn read_fields() {
        let buffer = [
            0x00, 0x35, // source port: 53 (DNS)
            0xC3, 0x50, // destination port: 50000
            0x00, 0x10, // length: 16
            0x12, 0x34, // checksum: 0x1234
        ];

        let mapping = HeaderMapping::new(&buffer[..]);

        assert_eq!(mapping.read_source_port(), Port::DNS);
        assert_eq!(mapping.read_destination_port(), Port::from(50000));
        assert_eq!(mapping.read_length(), 16);
        assert_eq!(mapping.read_checksum(), Checksum(0x1234));
    }

    #[test]
    fn write_fields() {
        let mut buffer = [0u8; 8];
        let mut mapping = HeaderMapping::new(&mut buffer[..]);

        mapping.write_source_port(Port::DNS).unwrap();
        mapping.write_destination_port(Port::from(50000)).unwrap();
        mapping.write_length(16).unwrap();
        mapping.write_checksum(Checksum(0x1234)).unwrap();

        assert_eq!(buffer, [0x00, 0x35, 0xC3, 0x50, 0x00, 0x10, 0x12, 0x34,]);
    }

    #[test]
    fn roundtrip() {
        let mut buffer = [0u8; 8];
        let mut mapping = HeaderMapping::new(&mut buffer[..]);

        let source_port = Port::DNS;
        let destination_port = Port::from(50000);
        let length = 16u16;
        let checksum = Checksum(0x1234);

        mapping.write_source_port(source_port).unwrap();
        mapping.write_destination_port(destination_port).unwrap();
        mapping.write_length(length).unwrap();
        mapping.write_checksum(checksum).unwrap();

        let read_mapping = HeaderMapping::new(&buffer[..]);

        assert_eq!(read_mapping.read_source_port(), source_port);
        assert_eq!(read_mapping.read_destination_port(), destination_port);
        assert_eq!(read_mapping.read_length(), length);
        assert_eq!(read_mapping.read_checksum(), checksum);
    }

    #[test]
    fn into_inner() {
        let buffer = [0u8; 8];
        let mapping = HeaderMapping::new(&buffer[..]);
        let inner = mapping.into_inner();
        assert_eq!(inner.len(), 8);
    }
}