1#[cfg(not(feature = "std"))]
37use alloc::vec::Vec;
38
39use bacnet_types::bitstring::{DaysOfWeek, EventTransitionBits};
40use bacnet_types::constructed::{BACnetAddress, BACnetDestination, BACnetRecipient};
41use bacnet_types::error::{DecodeError, DecodingKind, EncodeError, Error};
42use bacnet_types::primitives::Time;
43use bacnet_types::MacAddr;
44use bytes::BytesMut;
45
46use crate::primitives;
47use crate::tags::{self, TagClass};
48
49use super::tagged::{
50 contents, decode_app_bit_string, decode_app_unsigned, decode_ctx_object_id, expect_closing,
51 misplaced,
52};
53use super::MAX_FRAMED_ITEMS;
54
55fn mac_len_excess(length: usize) -> bool {
58 length > BACnetAddress::MAX_MAC_LEN
59}
60
61pub fn check_decoded_mac_len(length: usize, offset: usize, what: &'static str) -> Result<(), Error> {
67 if !mac_len_excess(length) {
68 return Ok(());
69 }
70 Err(DecodeError::new(
71 DecodingKind::Overflow,
72 offset,
73 "mac-address of {} octets exceeds the {}-octet limit",
74 )
75 .within(what)
76 .with_values(length as u64, BACnetAddress::MAX_MAC_LEN as u64)
77 .into())
78}
79
80pub fn check_encoded_mac_len(mac: &[u8], what: &'static str) -> Result<(), Error> {
84 if !mac_len_excess(mac.len()) {
85 return Ok(());
86 }
87 Err(
88 EncodeError::new("mac-address of {} octets exceeds the {}-octet limit")
89 .within(what)
90 .with_values(mac.len() as u32, BACnetAddress::MAX_MAC_LEN as u32)
91 .into(),
92 )
93}
94
95pub(super) fn check_encoded_recipient(recipient: &BACnetRecipient) -> Result<(), Error> {
98 match recipient {
99 BACnetRecipient::Device(_) => Ok(()),
100 BACnetRecipient::Address(address) => {
101 check_encoded_mac_len(&address.mac_address, "BACnetRecipient")
102 }
103 }
104}
105
106pub fn encode_destination(buf: &mut BytesMut, dest: &BACnetDestination) -> Result<(), Error> {
115 check_encoded_recipient(&dest.recipient)?;
116 write_destination(buf, dest);
117 Ok(())
118}
119
120fn write_destination(buf: &mut BytesMut, dest: &BACnetDestination) {
122 primitives::encode_app_bit_string(buf, 1, &[dest.valid_days.to_bacnet()]);
123 primitives::encode_app_time(buf, &dest.from_time);
124 primitives::encode_app_time(buf, &dest.to_time);
125 write_recipient(buf, &dest.recipient);
126 primitives::encode_app_unsigned(buf, dest.process_identifier as u64);
127 primitives::encode_app_boolean(buf, dest.issue_confirmed_notifications);
128 primitives::encode_app_bit_string(buf, 5, &[dest.transitions.to_bacnet()]);
129}
130
131pub fn encode_destination_list(
135 buf: &mut BytesMut,
136 destinations: &[BACnetDestination],
137) -> Result<(), Error> {
138 for dest in destinations {
139 check_encoded_recipient(&dest.recipient)?;
140 }
141 for dest in destinations {
142 write_destination(buf, dest);
143 }
144 Ok(())
145}
146
147pub fn encode_recipient(buf: &mut BytesMut, recipient: &BACnetRecipient) -> Result<(), Error> {
151 check_encoded_recipient(recipient)?;
152 write_recipient(buf, recipient);
153 Ok(())
154}
155
156pub(super) fn write_recipient(buf: &mut BytesMut, recipient: &BACnetRecipient) {
158 match recipient {
159 BACnetRecipient::Device(oid) => {
160 tags::encode_tag(buf, 0, TagClass::Context, 4);
162 buf.extend_from_slice(&oid.encode());
163 }
164 BACnetRecipient::Address(address) => {
165 tags::encode_opening_tag(buf, 1);
167 primitives::encode_app_unsigned(buf, address.network_number as u64);
168 primitives::encode_app_octet_string(buf, &address.mac_address);
169 tags::encode_closing_tag(buf, 1);
170 }
171 }
172}
173
174pub fn decode_recipient(data: &[u8], offset: usize) -> Result<(BACnetRecipient, usize), Error> {
183 let what = "BACnetRecipient";
184 let (tag, pos) = tags::decode_tag(data, offset)?;
185 if tag.is_context(0) {
186 let (device, end) = decode_ctx_object_id(data, offset, 0, "BACnetRecipient device")?;
188 return Ok((BACnetRecipient::Device(device), end));
189 }
190 if tag.is_opening_tag(1) {
191 let (network_number, pos) = decode_app_unsigned::<u64>(data, pos, what)?;
193 let network_number = u16::try_from(network_number).map_err(|_| {
194 DecodeError::new(
195 DecodingKind::OutOfRange,
196 pos,
197 "network-number exceeds Unsigned16",
198 )
199 .within(what)
200 })?;
201 let (mac_address, pos) = decode_app_mac_address(data, pos, what)?;
203 let end = expect_closing(data, pos, 1, "BACnetRecipient address")?;
204 return Ok((
205 BACnetRecipient::Address(BACnetAddress {
206 network_number,
207 mac_address,
208 }),
209 end,
210 ));
211 }
212 let label = if tag.is_opening {
213 "expected [0] (device) or [1] (address), got opening tag [{}]"
214 } else if tag.is_closing {
215 "expected [0] (device) or [1] (address), got closing tag [{}]"
216 } else {
217 match tag.class {
218 TagClass::Context => "expected [0] (device) or [1] (address), got primitive context tag [{}]",
219 TagClass::Application => "expected [0] (device) or [1] (address), got application tag {}",
220 }
221 };
222 Err(misplaced(data, &tag, None, offset, label)
223 .within(what)
224 .with_value(tag.number.into())
225 .into())
226}
227
228pub fn decode_destination(data: &[u8], offset: usize) -> Result<(BACnetDestination, usize), Error> {
233 let what = "BACnetDestination";
234 let ((days_unused, days_data), pos) = decode_app_bit_string(data, offset, what)?;
236 check_fixed_bit_string(
237 days_unused,
238 &days_data,
239 1,
240 "valid-days (BACnetDaysOfWeek: 7 bits)",
241 )?;
242 let valid_days = DaysOfWeek::from_bacnet(&days_data);
243 let (from_time, pos) = decode_app_time(data, pos, what)?;
245 let (to_time, pos) = decode_app_time(data, pos, what)?;
246 let (recipient, pos) = decode_recipient(data, pos)?;
248 let (process_identifier, pos) = decode_app_unsigned::<u64>(data, pos, what)?;
250 let process_identifier = u32::try_from(process_identifier).map_err(|_| {
251 DecodeError::new(
252 DecodingKind::OutOfRange,
253 pos,
254 "process-identifier exceeds Unsigned32",
255 )
256 .within(what)
257 })?;
258 let (tag, pos) = tags::decode_tag(data, pos)?;
261 if tag.class != TagClass::Application || tag.number != tags::app_tag::BOOLEAN {
262 return Err(
263 misplaced(data, &tag, None, pos, "expected application-tagged BOOLEAN")
264 .within(what)
265 .into(),
266 );
267 }
268 let issue_confirmed_notifications = tag.length != 0;
269 let ((transitions_unused, transitions_data), pos) = decode_app_bit_string(data, pos, what)?;
271 check_fixed_bit_string(
272 transitions_unused,
273 &transitions_data,
274 5,
275 "transitions (BACnetEventTransitionBits: 3 bits)",
276 )?;
277 let transitions = EventTransitionBits::from_bacnet(&transitions_data);
278
279 Ok((
280 BACnetDestination {
281 valid_days,
282 from_time,
283 to_time,
284 recipient,
285 process_identifier,
286 issue_confirmed_notifications,
287 transitions,
288 },
289 pos,
290 ))
291}
292
293pub fn decode_destination_list(data: &[u8]) -> Result<Vec<BACnetDestination>, Error> {
296 let mut destinations = Vec::new();
297 let mut pos = 0;
298 while pos < data.len() {
299 if destinations.len() >= MAX_FRAMED_ITEMS {
300 return Err(Error::overflow(
301 pos,
302 "Recipient_List: destination count exceeds limit",
303 ));
304 }
305 let (dest, new_pos) = decode_destination(data, pos)?;
306 destinations.push(dest);
307 pos = new_pos;
308 }
309 Ok(destinations)
310}
311
312pub(super) fn decode_app_mac_address(
320 data: &[u8],
321 offset: usize,
322 what: &'static str,
323) -> Result<(MacAddr, usize), Error> {
324 let (tag, pos) = tags::decode_tag(data, offset)?;
325 if tag.class != TagClass::Application || tag.number != tags::app_tag::OCTET_STRING {
326 return Err(misplaced(
327 data,
328 &tag,
329 None,
330 offset,
331 "expected application-tagged OCTET STRING",
332 )
333 .within(what)
334 .into());
335 }
336 check_decoded_mac_len(tag.length as usize, offset, what)?;
337 let (mac, end) = contents(data, pos, tag.length)?;
338 Ok((MacAddr::from_slice(mac), end))
339}
340
341fn decode_app_time(data: &[u8], offset: usize, what: &'static str) -> Result<(Time, usize), Error> {
343 let (tag, pos) = tags::decode_tag(data, offset)?;
344 if tag.class != TagClass::Application || tag.number != tags::app_tag::TIME {
345 return Err(
346 misplaced(data, &tag, None, offset, "expected application-tagged Time")
347 .within(what)
348 .into(),
349 );
350 }
351 if tag.length != 4 {
352 return Err(DecodeError::new(
353 DecodingKind::InvalidEncoding,
354 offset,
355 "Time has {} contents octets, expected 4",
356 )
357 .within(what)
358 .with_value(tag.length.into())
359 .into());
360 }
361 let (octets, end) = contents(data, pos, 4)?;
362 Ok((Time::decode(octets)?, end))
363}
364
365fn check_fixed_bit_string(
374 unused_bits: u8,
375 data: &[u8],
376 expected_unused: u8,
377 name: &'static str,
378) -> Result<(), Error> {
379 if data.len() != 1 {
380 return Err(DecodeError::new(
381 DecodingKind::InvalidEncoding,
382 0,
383 "expected 1 content octet, got {}",
384 )
385 .within(name)
386 .with_value(data.len() as u64)
387 .into());
388 }
389 if unused_bits != expected_unused {
390 return Err(DecodeError::new(
391 DecodingKind::InvalidEncoding,
392 0,
393 "expected unused_bits {}, got {}",
394 )
395 .within(name)
396 .with_values(expected_unused.into(), unused_bits.into())
397 .into());
398 }
399 Ok(())
400}