Skip to main content

ace_doip/
ext.rs

1use crate::{
2    error::DoipValidationError,
3    header::{PayloadType, ProtocolVersion},
4};
5use ace_proto::{
6    common::{RawFrame, RawFrameMut},
7    doip::constants::{
8        DOIP_HEADER_LEN, DOIP_INV_VERSION_OFFSET, DOIP_LENGTH_OFFSET, DOIP_TYPE_OFFSET,
9        DOIP_VERSION_OFFSET,
10    },
11    DoipFrame, DoipFrameMut,
12};
13
14// region: DoipFrameExt
15
16pub trait DoipFrameExt {
17    fn as_bytes(&self) -> &[u8];
18
19    // region: Raw header field accessors
20
21    fn protocol_version(&self) -> Option<u8> {
22        self.as_bytes().get(DOIP_VERSION_OFFSET).copied()
23    }
24
25    fn inverse_protocol_version(&self) -> Option<u8> {
26        self.as_bytes().get(DOIP_INV_VERSION_OFFSET).copied()
27    }
28
29    fn payload_type_raw(&self) -> Option<u16> {
30        let b = self.as_bytes();
31        if b.len() < DOIP_TYPE_OFFSET {
32            return None;
33        }
34        Some(u16::from_be_bytes([
35            b[DOIP_TYPE_OFFSET],
36            b[DOIP_TYPE_OFFSET + 1],
37        ]))
38    }
39
40    fn payload_length_declared(&self) -> Option<u32> {
41        let b = self.as_bytes();
42        if b.len() < DOIP_HEADER_LEN {
43            return None;
44        }
45        Some(u32::from_be_bytes([
46            b[DOIP_LENGTH_OFFSET],
47            b[DOIP_LENGTH_OFFSET + 1],
48            b[DOIP_LENGTH_OFFSET + 2],
49            b[DOIP_LENGTH_OFFSET + 3],
50        ]))
51    }
52
53    fn payload_bytes(&self) -> Option<&[u8]> {
54        let b = self.as_bytes();
55        if b.len() < DOIP_HEADER_LEN {
56            return None;
57        }
58        Some(&b[DOIP_HEADER_LEN..])
59    }
60
61    // endregion: Raw header field accessors
62
63    // region: Validation
64
65    /// Checks all ISO 13400-2 header invariants and returns the first violation
66    /// found, or Ok(()) if the header is well-formed.
67    fn validate_header(&self) -> Result<(), DoipValidationError> {
68        let b = self.as_bytes();
69
70        // Must have at least 8 bytes to contain a complete DoIP header
71        if b.len() < DOIP_HEADER_LEN {
72            return Err(DoipValidationError::FrameTooShort { actual: b.len() });
73        }
74
75        // Protocol version must be a known ISO 13400-2 value
76        let version = ProtocolVersion::try_from(b[DOIP_VERSION_OFFSET])
77            .map_err(|_| DoipValidationError::UnsupportedProtocolVersion(b[DOIP_VERSION_OFFSET]))?;
78
79        // Inverse protocol version must be the bitwise complement of the version byte
80        let inverse = b[DOIP_INV_VERSION_OFFSET];
81        if inverse != !(version as u8) {
82            return Err(DoipValidationError::InvalidInverseProtocolVersion { version, inverse });
83        }
84
85        // Payload type must be a known ISO 13400-2 value
86        let payload_type_raw = u16::from_be_bytes([b[DOIP_TYPE_OFFSET], b[DOIP_TYPE_OFFSET + 1]]);
87        PayloadType::try_from(payload_type_raw)
88            .map_err(|_| DoipValidationError::UnknownPayloadType(payload_type_raw))?;
89
90        // Declared payload length must match actual bytes following the header
91        let declared = u32::from_be_bytes([
92            b[DOIP_LENGTH_OFFSET],
93            b[DOIP_LENGTH_OFFSET + 1],
94            b[DOIP_LENGTH_OFFSET + 2],
95            b[DOIP_LENGTH_OFFSET + 3],
96        ]) as usize;
97        let actual = b.len() - DOIP_HEADER_LEN;
98
99        if declared != actual {
100            return Err(DoipValidationError::PayloadLengthMismatch {
101                declared: declared as u32,
102                actual,
103            });
104        }
105
106        Ok(())
107    }
108
109    /// Returns true if the header passes all ISO 13400-2 invariants.
110    fn is_valid(&self) -> bool {
111        self.validate_header().is_ok()
112    }
113
114    // endregion: Validation
115
116    // region: Typed payload type
117
118    fn payload_type(&self) -> Option<Result<PayloadType, DoipValidationError>> {
119        self.payload_type_raw().map(|raw| {
120            PayloadType::try_from(raw).map_err(|_| DoipValidationError::UnknownPayloadType(raw))
121        })
122    }
123    // endregion: Typed payload type
124}
125
126// endregion: DoipFrameExt
127
128impl<'a> DoipFrameExt for DoipFrame<'a> {
129    fn as_bytes(&self) -> &[u8] {
130        RawFrame::as_bytes(self)
131    }
132}
133
134impl<'a> DoipFrameExt for DoipFrameMut<'a> {
135    fn as_bytes(&self) -> &[u8] {
136        RawFrame::as_bytes(self)
137    }
138}
139
140impl<'a> DoipFrameMutExt for DoipFrameMut<'a> {
141    fn as_bytes_mut(&mut self) -> &mut [u8] {
142        RawFrameMut::as_bytes_mut(self)
143    }
144}
145
146// region: DoipFrameMutExt
147
148pub trait DoipFrameMutExt: DoipFrameExt {
149    fn as_bytes_mut(&mut self) -> &mut [u8];
150
151    fn set_protocol_version(&mut self, version: ProtocolVersion) {
152        let v = version as u8;
153
154        if let Some(b) = self.as_bytes_mut().get_mut(DOIP_VERSION_OFFSET) {
155            *b = v;
156        }
157
158        if let Some(b) = self.as_bytes_mut().get_mut(DOIP_INV_VERSION_OFFSET) {
159            *b = !v;
160        }
161    }
162
163    fn set_payload_type(&mut self, payload_type: PayloadType) {
164        let raw = payload_type as u16;
165        let bytes = raw.to_be_bytes();
166
167        if let Some(b) = self.as_bytes_mut().get_mut(DOIP_TYPE_OFFSET) {
168            *b = bytes[0];
169        }
170
171        if let Some(b) = self.as_bytes_mut().get_mut(DOIP_TYPE_OFFSET + 1) {
172            *b = bytes[1];
173        }
174    }
175
176    fn set_payload_length(&mut self, length: u32) {
177        let bytes = length.to_be_bytes();
178        for (i, &byte) in bytes.iter().enumerate() {
179            if let Some(b) = self.as_bytes_mut().get_mut(DOIP_LENGTH_OFFSET + i) {
180                *b = byte;
181            }
182        }
183    }
184}
185
186// endregion: DoipFrameMutExt