Skip to main content

etherparse/err/packet/
build_write_error.rs

1#[cfg(feature = "std")]
2use crate::err::{ipv4_exts, ipv6_exts, ValueTooBigError};
3
4/// Error while writing packet
5#[cfg(feature = "std")]
6#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
7#[derive(Debug)]
8pub enum BuildWriteError {
9    /// IO error while writing packet.
10    Io(std::io::Error),
11
12    /// Error if the length of the payload is too
13    /// big to be representable by the length fields.
14    PayloadLen(ValueTooBigError<usize>),
15
16    /// Error if the IPv4 extensions can not be serialized
17    /// because of internal consistency errors.
18    Ipv4Exts(ipv4_exts::ExtsWalkError),
19
20    /// Error if the IPv6 extensions can not be serialized
21    /// because of internal consistency errors.
22    Ipv6Exts(ipv6_exts::ExtsWalkError),
23
24    /// Error if ICMPv6 is packaged in an IPv4 packet (it is undefined
25    /// how to calculate the checksum).
26    Icmpv6InIpv4,
27
28    /// address size defined in the ARP header does not match the actual size
29    ArpHeaderNotMatch,
30}
31
32#[cfg(feature = "std")]
33#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
34impl BuildWriteError {
35    /// Returns the [`std::io::Error`] value if the `BuildWriteError` is an `Io`.
36    /// Otherwise `None` is returned.
37    pub fn io(&self) -> Option<&std::io::Error> {
38        match self {
39            BuildWriteError::Io(err) => Some(err),
40            _ => None,
41        }
42    }
43
44    /// Returns the [`crate::err::ValueTooBigError`] value if the
45    /// `BuildWriteError` is a `PayloadLen`. Otherwise `None` is returned.
46    pub fn payload_len(&self) -> Option<&ValueTooBigError<usize>> {
47        match self {
48            BuildWriteError::PayloadLen(err) => Some(err),
49            _ => None,
50        }
51    }
52
53    /// Returns the [`crate::err::ipv4_exts::ExtsWalkError`] value if the
54    /// `BuildWriteError` is a `Ipv4Exts`. Otherwise `None` is returned.
55    pub fn ipv4_exts(&self) -> Option<&ipv4_exts::ExtsWalkError> {
56        match self {
57            BuildWriteError::Ipv4Exts(err) => Some(err),
58            _ => None,
59        }
60    }
61
62    /// Returns the [`crate::err::ipv6_exts::ExtsWalkError`] value if the
63    /// `BuildWriteError` is a `Ipv6Exts`. Otherwise `None` is returned.
64    pub fn ipv6_exts(&self) -> Option<&ipv6_exts::ExtsWalkError> {
65        match self {
66            BuildWriteError::Ipv6Exts(err) => Some(err),
67            _ => None,
68        }
69    }
70
71    /// Returns true if the `BuildWriteError` is a `Icmpv6InIpv4`.
72    pub fn is_icmpv6_in_ipv4(&self) -> bool {
73        matches!(self, BuildWriteError::Icmpv6InIpv4)
74    }
75}
76
77#[cfg(feature = "std")]
78#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
79impl core::fmt::Display for BuildWriteError {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        use BuildWriteError::*;
82        match self {
83            Io(err) => err.fmt(f),
84            PayloadLen(err) => err.fmt(f),
85            Ipv4Exts(err) => err.fmt(f),
86            Ipv6Exts(err) => err.fmt(f),
87            ArpHeaderNotMatch => write!(f, "address size defined in the ARP header does not match the actual size"),
88            Icmpv6InIpv4 => write!(f, "Error: ICMPv6 can not be combined with an IPv4 headers (checksum can not be calculated)."),
89        }
90    }
91}
92
93impl core::error::Error for BuildWriteError {
94    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
95        use BuildWriteError::*;
96        match self {
97            Io(ref err) => Some(err),
98            PayloadLen(ref err) => Some(err),
99            Ipv4Exts(err) => Some(err),
100            Ipv6Exts(err) => Some(err),
101            Icmpv6InIpv4 => None,
102            ArpHeaderNotMatch => None,
103        }
104    }
105}
106
107#[cfg(feature = "std")]
108impl From<std::io::Error> for BuildWriteError {
109    fn from(value: std::io::Error) -> Self {
110        BuildWriteError::Io(value)
111    }
112}
113
114#[cfg(feature = "std")]
115impl From<ValueTooBigError<usize>> for BuildWriteError {
116    fn from(value: ValueTooBigError<usize>) -> Self {
117        BuildWriteError::PayloadLen(value)
118    }
119}
120
121#[cfg(feature = "std")]
122impl From<super::TransportChecksumError> for BuildWriteError {
123    fn from(value: super::TransportChecksumError) -> Self {
124        match value {
125            super::TransportChecksumError::PayloadLen(err) => BuildWriteError::PayloadLen(err),
126            super::TransportChecksumError::Icmpv6InIpv4 => BuildWriteError::Icmpv6InIpv4,
127        }
128    }
129}
130
131#[cfg(feature = "std")]
132impl From<crate::WriteError<std::io::Error, ipv4_exts::ExtsWalkError>> for BuildWriteError {
133    fn from(value: crate::WriteError<std::io::Error, ipv4_exts::ExtsWalkError>) -> Self {
134        match value {
135            crate::WriteError::Io(err) => BuildWriteError::Io(err),
136            crate::WriteError::Content(err) => BuildWriteError::Ipv4Exts(err),
137        }
138    }
139}
140
141#[cfg(feature = "std")]
142impl From<crate::WriteError<std::io::Error, ipv6_exts::ExtsWalkError>> for BuildWriteError {
143    fn from(value: crate::WriteError<std::io::Error, ipv6_exts::ExtsWalkError>) -> Self {
144        match value {
145            crate::WriteError::Io(err) => BuildWriteError::Io(err),
146            crate::WriteError::Content(err) => BuildWriteError::Ipv6Exts(err),
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::{BuildWriteError::*, *};
154    use crate::{err::ValueType, *};
155    use alloc::format;
156    use core::error::Error;
157
158    #[test]
159    fn io() {
160        assert!(Io(std::io::Error::new(
161            std::io::ErrorKind::UnexpectedEof,
162            "failed to fill whole buffer",
163        ))
164        .io()
165        .is_some());
166        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
167            missing_ext: IpNumber::AUTHENTICATION_HEADER,
168        })
169        .io()
170        .is_none());
171    }
172
173    #[test]
174    fn payload_len() {
175        {
176            let err = ValueTooBigError {
177                actual: 3,
178                max_allowed: 2,
179                value_type: ValueType::Ipv4PayloadLength,
180            };
181            assert_eq!(Some(&err), PayloadLen(err.clone()).payload_len());
182        }
183        {
184            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
185                missing_ext: IpNumber::AUTHENTICATION_HEADER,
186            };
187            assert_eq!(None, Ipv4Exts(err.clone()).payload_len());
188        }
189    }
190
191    #[test]
192    fn ipv4_exts() {
193        assert!(Io(std::io::Error::new(
194            std::io::ErrorKind::UnexpectedEof,
195            "failed to fill whole buffer",
196        ))
197        .ipv4_exts()
198        .is_none());
199        {
200            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
201                missing_ext: IpNumber::AUTHENTICATION_HEADER,
202            };
203            assert_eq!(Some(&err), Ipv4Exts(err.clone()).ipv4_exts());
204        }
205    }
206
207    #[test]
208    fn ipv6_exts() {
209        assert!(Io(std::io::Error::new(
210            std::io::ErrorKind::UnexpectedEof,
211            "failed to fill whole buffer",
212        ))
213        .ipv6_exts()
214        .is_none());
215        {
216            let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
217                missing_ext: IpNumber::AUTHENTICATION_HEADER,
218            };
219            assert_eq!(Some(&err), Ipv6Exts(err.clone()).ipv6_exts());
220        }
221    }
222
223    #[test]
224    fn is_icmpv6_in_ipv4() {
225        assert_eq!(
226            false,
227            Io(std::io::Error::new(
228                std::io::ErrorKind::UnexpectedEof,
229                "failed to fill whole buffer",
230            ))
231            .is_icmpv6_in_ipv4()
232        );
233        assert!(Icmpv6InIpv4.is_icmpv6_in_ipv4());
234    }
235
236    #[test]
237    fn debug() {
238        let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
239            missing_ext: IpNumber::AUTHENTICATION_HEADER,
240        };
241        assert_eq!(
242            format!("Ipv4Exts({:?})", err.clone()),
243            format!("{:?}", Ipv4Exts(err))
244        );
245    }
246
247    #[test]
248    fn fmt() {
249        {
250            let err = std::io::Error::new(
251                std::io::ErrorKind::UnexpectedEof,
252                "failed to fill whole buffer",
253            );
254            assert_eq!(format!("{}", err), format!("{}", Io(err)));
255        }
256        {
257            let err = ValueTooBigError {
258                actual: 3,
259                max_allowed: 2,
260                value_type: ValueType::Ipv4PayloadLength,
261            };
262            assert_eq!(format!("{}", err), format!("{}", PayloadLen(err.clone())));
263        }
264        {
265            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
266                missing_ext: IpNumber::AUTHENTICATION_HEADER,
267            };
268            assert_eq!(format!("{}", err), format!("{}", Ipv4Exts(err.clone())));
269        }
270        {
271            let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
272                missing_ext: IpNumber::AUTHENTICATION_HEADER,
273            };
274            assert_eq!(format!("{}", err), format!("{}", Ipv6Exts(err.clone())));
275        }
276        assert_eq!(
277            "Error: ICMPv6 can not be combined with an IPv4 headers (checksum can not be calculated).",
278            format!("{}", Icmpv6InIpv4)
279        );
280    }
281
282    #[cfg(feature = "std")]
283    #[test]
284    fn source() {
285        assert!(Io(std::io::Error::new(
286            std::io::ErrorKind::UnexpectedEof,
287            "failed to fill whole buffer",
288        ))
289        .source()
290        .is_some());
291        assert!(PayloadLen(ValueTooBigError {
292            actual: 3,
293            max_allowed: 2,
294            value_type: ValueType::Ipv4PayloadLength,
295        })
296        .source()
297        .is_some());
298        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
299            missing_ext: IpNumber::AUTHENTICATION_HEADER,
300        })
301        .source()
302        .is_some());
303        assert!(Ipv6Exts(ipv6_exts::ExtsWalkError::ExtNotReferenced {
304            missing_ext: IpNumber::AUTHENTICATION_HEADER,
305        })
306        .source()
307        .is_some());
308        assert!(Icmpv6InIpv4.source().is_none());
309    }
310}