simple_someip/protocol/sd/
header.rs1use crate::protocol::byte_order::WriteBytesExt;
2
3use crate::traits::WireFormat;
4
5use super::{
6 Entry, Flags, Options,
7 entry::{ENTRY_SIZE, EntryIter, EntryType},
8 options::{OptionIter, validate_option},
9};
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub struct Header<'a> {
17 pub flags: Flags,
19 pub entries: &'a [Entry],
21 pub options: &'a [Options],
23}
24
25impl<'a> Header<'a> {
26 #[must_use]
28 pub const fn new(flags: Flags, entries: &'a [Entry], options: &'a [Options]) -> Self {
29 Self {
30 flags,
31 entries,
32 options,
33 }
34 }
35}
36
37#[derive(Clone, Copy, Debug)]
43pub struct SdHeaderView<'a> {
44 flags: Flags,
45 entries_buf: &'a [u8],
46 options_buf: &'a [u8],
47}
48
49impl<'a> SdHeaderView<'a> {
50 pub fn parse(buf: &'a [u8]) -> Result<Self, crate::protocol::Error> {
65 if buf.len() < 12 {
67 return Err(crate::protocol::Error::UnexpectedEof);
68 }
69
70 let flags = Flags::from(buf[0]);
71 let entries_size = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]) as usize;
74
75 if !entries_size.is_multiple_of(ENTRY_SIZE) {
76 return Err(super::Error::IncorrectEntriesSize(entries_size).into());
77 }
78
79 if buf.len() < 8 + entries_size + 4 {
81 return Err(crate::protocol::Error::UnexpectedEof);
82 }
83
84 let entries_buf = &buf[8..8 + entries_size];
85
86 let mut offset = 0;
88 while offset < entries_size {
89 EntryType::try_from(entries_buf[offset])?;
90 offset += ENTRY_SIZE;
91 }
92
93 let options_size_offset = 8 + entries_size;
94 let options_size = u32::from_be_bytes([
95 buf[options_size_offset],
96 buf[options_size_offset + 1],
97 buf[options_size_offset + 2],
98 buf[options_size_offset + 3],
99 ]) as usize;
100
101 let options_start = options_size_offset + 4;
102 if buf.len() < options_start + options_size {
103 return Err(crate::protocol::Error::UnexpectedEof);
104 }
105
106 let options_buf = &buf[options_start..options_start + options_size];
107
108 let mut opt_offset = 0;
110 while opt_offset < options_size {
111 let remaining = &options_buf[opt_offset..];
112 let wire_size = validate_option(remaining)?;
113 opt_offset += wire_size;
114 }
115
116 Ok(Self {
117 flags,
118 entries_buf,
119 options_buf,
120 })
121 }
122
123 #[must_use]
125 pub fn flags(&self) -> Flags {
126 self.flags
127 }
128
129 #[must_use]
131 pub fn entries(&self) -> EntryIter<'a> {
132 EntryIter::new(self.entries_buf)
133 }
134
135 #[must_use]
137 pub fn options(&self) -> OptionIter<'a> {
138 OptionIter::new(self.options_buf)
139 }
140
141 #[must_use]
143 pub fn entry_count(&self) -> usize {
144 self.entries_buf.len() / ENTRY_SIZE
145 }
146}
147
148impl WireFormat for Header<'_> {
149 fn required_size(&self) -> usize {
150 let mut size = 12 + self.entries.len() * ENTRY_SIZE;
151 for option in self.options {
152 size += option.size();
153 }
154 size
155 }
156
157 fn encode<T: embedded_io::Write>(
158 &self,
159 writer: &mut T,
160 ) -> Result<usize, crate::protocol::Error> {
161 writer.write_u8(u8::from(self.flags))?;
162 let reserved: [u8; 3] = [0; 3];
163 writer.write_bytes(&reserved)?;
164 let entries_size = u32::try_from(self.entries.len() * 16).expect("entries size fits u32");
165 writer.write_u32_be(entries_size)?;
166 for entry in self.entries {
167 entry.encode(writer)?;
168 }
169 let mut options_size = 0;
170 for option in self.options {
171 options_size += option.size();
172 }
173 writer.write_u32_be(u32::try_from(options_size).expect("options size fits u32"))?;
174 for option in self.options {
175 option.write(writer)?;
176 }
177 Ok(12 + entries_size as usize + options_size)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use core::net::Ipv4Addr;
184
185 use super::*;
186 use crate::{
187 protocol::sd::{
188 Error as SdError, EventGroupEntry, OptionType, OptionsCount, ServiceEntry,
189 TransportProtocol,
190 options::{
191 IPV4_OPTION_IP_OFFSET, IPV4_OPTION_LENGTH_FIELD, IPV4_OPTION_PORT_OFFSET,
192 IPV4_OPTION_PROTOCOL_OFFSET, IPV4_OPTION_WIRE_SIZE,
193 },
194 },
195 traits::WireFormat,
196 };
197
198 fn ipv4_endpoint_bytes(ip: [u8; 4], protocol: u8, port: u16) -> [u8; IPV4_OPTION_WIRE_SIZE] {
199 let mut b = [0u8; IPV4_OPTION_WIRE_SIZE];
200 b[0..2].copy_from_slice(&IPV4_OPTION_LENGTH_FIELD.to_be_bytes());
201 b[2] = u8::from(OptionType::IpV4Endpoint);
202 b[IPV4_OPTION_IP_OFFSET..IPV4_OPTION_IP_OFFSET + 4].copy_from_slice(&ip);
204 b[IPV4_OPTION_PROTOCOL_OFFSET] = protocol;
206 b[IPV4_OPTION_PORT_OFFSET..IPV4_OPTION_PORT_OFFSET + 2]
207 .copy_from_slice(&port.to_be_bytes());
208 b
209 }
210
211 fn raw_header(entries_size: u32, options_size: u32) -> [u8; 12] {
212 let mut b = [0u8; 12];
213 b[4..8].copy_from_slice(&entries_size.to_be_bytes());
215 b[8..12].copy_from_slice(&options_size.to_be_bytes());
216 b
217 }
218
219 #[test]
220 fn header_new_stores_fields() {
221 let flags = Flags::new_sd(true);
222 let entries: &[Entry] = &[];
223 let options: &[Options] = &[];
224 let h = Header::new(flags, entries, options);
225 assert_eq!(h.flags, flags);
226 assert!(h.entries.is_empty());
227 assert!(h.options.is_empty());
228 }
229
230 #[test]
231 fn service_offer_round_trips() {
232 let ip = Ipv4Addr::new(192, 168, 1, 10);
233 let entry = Entry::OfferService(ServiceEntry {
234 service_id: 0x1234,
235 instance_id: 0x0001,
236 major_version: 1,
237 ttl: 0xFFFFFF,
238 index_first_options_run: 0,
239 index_second_options_run: 0,
240 options_count: OptionsCount::new(1, 0),
241 minor_version: 0,
242 });
243 let endpoint = Options::IpV4Endpoint {
244 ip,
245 protocol: TransportProtocol::Udp,
246 port: 30509,
247 };
248 let entries = [entry];
249 let options = [endpoint];
250 let h = Header::new(Flags::new_sd(false), &entries, &options);
251 assert_eq!(h.required_size(), 40);
252 let mut buf = [0u8; 64];
253 h.encode(&mut buf.as_mut_slice()).unwrap();
254 let view = SdHeaderView::parse(&buf[..h.required_size()]).unwrap();
255 assert_eq!(view.entry_count(), 1);
256 let entry_view = view.entries().next().unwrap();
257 assert_eq!(entry_view.service_id(), 0x1234);
258 }
259
260 #[test]
261 fn subscribe_ack_round_trips() {
262 let entry = Entry::SubscribeAckEventGroup(EventGroupEntry::new(
263 0xAAAA, 0x0001, 1, 0xFFFFFF, 0x0010,
264 ));
265 let entries = [entry];
266 let h = Header::new(Flags::new_sd(true), &entries, &[]);
267 assert_eq!(h.required_size(), 28);
268 let mut buf = [0u8; 32];
269 h.encode(&mut buf.as_mut_slice()).unwrap();
270 let view = SdHeaderView::parse(&buf[..h.required_size()]).unwrap();
271 assert_eq!(view.entry_count(), 1);
272 }
273
274 #[test]
275 fn parse_exact_size_slice_succeeds() {
276 let entry = Entry::OfferService(ServiceEntry {
277 service_id: 0x1234,
278 instance_id: 0x0001,
279 major_version: 1,
280 ttl: 0xFFFFFF,
281 index_first_options_run: 0,
282 index_second_options_run: 0,
283 options_count: OptionsCount::new(1, 0),
284 minor_version: 0,
285 });
286 let endpoint = Options::IpV4Endpoint {
287 ip: Ipv4Addr::new(192, 168, 1, 10),
288 protocol: TransportProtocol::Udp,
289 port: 30509,
290 };
291 let entries = [entry];
292 let options = [endpoint];
293 let h = Header::new(Flags::new_sd(false), &entries, &options);
294 let mut buf = [0u8; 64];
295 let n = h.encode(&mut buf.as_mut_slice()).unwrap();
296 let view = SdHeaderView::parse(&buf[..n]).unwrap();
297 assert_eq!(view.entry_count(), 1);
298 }
299
300 #[test]
301 fn parse_options_size_below_minimum_returns_error() {
302 let prefix = raw_header(0, 2);
303 let mut buf = [0u8; 14];
304 buf[..12].copy_from_slice(&prefix);
305 assert!(matches!(
306 SdHeaderView::parse(&buf),
307 Err(crate::protocol::Error::Sd(SdError::IncorrectOptionsSize(2)))
308 ));
309 }
310
311 #[test]
312 fn parse_option_size_exceeds_declared_remaining_returns_error() {
313 let prefix = raw_header(0, 5);
314 let option = ipv4_endpoint_bytes([127, 0, 0, 1], 0x11, 1234);
315 let mut buf = [0u8; 24];
316 buf[..12].copy_from_slice(&prefix);
317 buf[12..24].copy_from_slice(&option);
318 assert!(matches!(
319 SdHeaderView::parse(&buf),
320 Err(crate::protocol::Error::Sd(SdError::IncorrectOptionsSize(5)))
321 ));
322 }
323
324 #[test]
327 fn sd_header_view_entry_count() {
328 let entries = [
329 Entry::FindService(ServiceEntry::find(0x0001)),
330 Entry::FindService(ServiceEntry::find(0x0002)),
331 ];
332 let h = Header::new(Flags::new_sd(false), &entries, &[]);
333 let mut buf = [0u8; 64];
334 h.encode(&mut buf.as_mut_slice()).unwrap();
335 let view = SdHeaderView::parse(&buf[..h.required_size()]).unwrap();
336 assert_eq!(view.entry_count(), 2);
337 }
338
339 #[test]
340 fn sd_header_view_flags() {
341 let h = Header::new(Flags::new_sd(true), &[], &[]);
342 let mut buf = [0u8; 16];
343 h.encode(&mut buf.as_mut_slice()).unwrap();
344 let view = SdHeaderView::parse(&buf[..h.required_size()]).unwrap();
345 assert_eq!(view.flags(), h.flags);
346 }
347
348 #[test]
349 fn parse_incorrect_entries_size_returns_error() {
350 let mut buf = [0u8; 12];
351 buf[4..8].copy_from_slice(&5u32.to_be_bytes());
352 assert!(matches!(
353 SdHeaderView::parse(&buf),
354 Err(crate::protocol::Error::Sd(SdError::IncorrectEntriesSize(5)))
355 ));
356 }
357
358 #[test]
359 fn parse_rejects_ipv4_option_with_invalid_transport_protocol() {
360 const SD_HEADER_PREFIX_SIZE: usize = 12;
365 let options_size = u32::try_from(IPV4_OPTION_WIRE_SIZE).expect("wire size fits u32");
366 let prefix = raw_header(0, options_size);
367 let option = ipv4_endpoint_bytes([10, 0, 0, 1], 0xAB, 30490);
368 let mut buf = [0u8; SD_HEADER_PREFIX_SIZE + IPV4_OPTION_WIRE_SIZE];
369 buf[..SD_HEADER_PREFIX_SIZE].copy_from_slice(&prefix);
370 buf[SD_HEADER_PREFIX_SIZE..].copy_from_slice(&option);
371 assert!(matches!(
372 SdHeaderView::parse(&buf),
373 Err(crate::protocol::Error::Sd(
374 SdError::InvalidOptionTransportProtocol(0xAB)
375 ))
376 ));
377 }
378}