Skip to main content

net_wire/icmpv4/
builder.rs

1//! ICMPv4 caller-buffer builder.
2
3use core::fmt;
4
5use super::message::{HEADER_LENGTH, Icmpv4MessageMut};
6use super::types::Icmpv4Type;
7
8/// Failure to construct an ICMPv4 message in caller-provided storage.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum Icmpv4MessageBuildError {
11    /// The type was not supplied.
12    MissingType,
13    /// The code was not supplied.
14    MissingCode,
15    /// The requested common header and body length cannot be represented as a `usize`.
16    MessageLengthTooLarge,
17    /// The buffer cannot hold the requested message.
18    BufferTooShort {
19        /// Required bytes.
20        required: usize,
21        /// Available bytes.
22        available: usize,
23    },
24}
25
26impl fmt::Display for Icmpv4MessageBuildError {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            Self::MissingType => formatter.write_str("missing ICMPv4 type"),
30            Self::MissingCode => formatter.write_str("missing ICMPv4 code"),
31            Self::MessageLengthTooLarge => {
32                formatter.write_str("ICMPv4 message length exceeds the platform limit")
33            }
34            Self::BufferTooShort {
35                required,
36                available,
37            } => write!(
38                formatter,
39                "ICMPv4 buffer is too short: need {required} bytes, have {available}"
40            ),
41        }
42    }
43}
44
45/// Builds an ICMPv4 common header in caller-owned storage.
46pub struct Icmpv4MessageBuilder<'a> {
47    buffer: &'a mut [u8],
48    body_length: usize,
49    message_type: Option<Icmpv4Type>,
50    code: Option<u8>,
51}
52
53impl<'a> Icmpv4MessageBuilder<'a> {
54    /// Starts a builder for `body_length` bytes, which are left untouched.
55    pub fn new(buffer: &'a mut [u8], body_length: usize) -> Self {
56        Self {
57            buffer,
58            body_length,
59            message_type: None,
60            code: None,
61        }
62    }
63    /// Supplies the ICMPv4 message type.
64    pub fn message_type(mut self, value: Icmpv4Type) -> Self {
65        self.message_type = Some(value);
66        self
67    }
68    /// Supplies the ICMPv4 message code.
69    pub fn code(mut self, value: u8) -> Self {
70        self.code = Some(value);
71        self
72    }
73    /// Validates all inputs, writes the common header, and computes its complete checksum.
74    ///
75    /// Body bytes and capacity beyond the requested message are preserved. On error, the whole
76    /// caller-provided buffer is unchanged.
77    pub fn build(self) -> Result<Icmpv4MessageMut<'a>, Icmpv4MessageBuildError> {
78        let message_type = self
79            .message_type
80            .ok_or(Icmpv4MessageBuildError::MissingType)?;
81        let code = self.code.ok_or(Icmpv4MessageBuildError::MissingCode)?;
82        let length = HEADER_LENGTH
83            .checked_add(self.body_length)
84            .ok_or(Icmpv4MessageBuildError::MessageLengthTooLarge)?;
85        if self.buffer.len() < length {
86            return Err(Icmpv4MessageBuildError::BufferTooShort {
87                required: length,
88                available: self.buffer.len(),
89            });
90        }
91        let bytes = &mut self.buffer[..length];
92        bytes[0] = message_type.raw();
93        bytes[1] = code;
94        bytes[2] = 0;
95        bytes[3] = 0;
96        let mut result = Icmpv4MessageMut::from_validated(bytes);
97        result.update_checksum();
98        Ok(result)
99    }
100}