1use bacnet_types::enums::{
14 AbortReason, ConfirmedServiceChoice, ErrorClass, ErrorCode, PduType, RejectReason,
15 UnconfirmedServiceChoice,
16};
17use bacnet_types::error::Error;
18use bytes::{BufMut, Bytes, BytesMut};
19
20use crate::primitives;
21use crate::tags;
22
23mod formal_error;
24
25const MAX_SEGMENTS_DECODE: [Option<u8>; 8] = [
32 None, Some(2), Some(4), Some(8), Some(16), Some(32), Some(64), Some(255), ];
41
42pub fn validate_max_segments(value: Option<u8>) -> Result<(), Error> {
48 match value {
49 Some(value @ 0..=1) => Err(Error::Encoding(format!(
50 "invalid max-segments-accepted {value}; expected 2..=255 or unspecified"
51 ))),
52 _ => Ok(()),
53 }
54}
55
56fn encode_max_segments(value: Option<u8>) -> Result<u8, Error> {
61 validate_max_segments(value)?;
62 Ok(match value {
63 None => 0,
64 Some(2..=3) => 1,
65 Some(4..=7) => 2,
66 Some(8..=15) => 3,
67 Some(16..=31) => 4,
68 Some(32..=63) => 5,
69 Some(64) => 6,
70 Some(65..=u8::MAX) => 7, Some(0..=1) => unreachable!("validated above"),
72 })
73}
74
75fn decode_max_segments(value: u8) -> Option<u8> {
77 MAX_SEGMENTS_DECODE[(value & 0x07) as usize]
78}
79
80pub fn advertised_max_segments(configured: Option<u8>) -> Option<u8> {
94 let encoded = encode_max_segments(configured).ok()?;
95 match decode_max_segments(encoded) {
96 Some(255) => None,
98 decoded => decoded,
99 }
100}
101
102pub const MINIMUM_MESSAGE_SIZE: u16 = 50;
113
114const MAX_APDU_DECODE: [u16; 6] = [MINIMUM_MESSAGE_SIZE, 128, 206, 480, 1024, 1476];
116
117pub fn max_apdu_header_at_or_below(capacity: u32) -> Result<u16, Error> {
124 MAX_APDU_DECODE
125 .iter()
126 .rev()
127 .copied()
128 .find(|value| u32::from(*value) <= capacity)
129 .ok_or_else(|| Error::Encoding(format!("local APDU capacity {capacity} is below 50")))
130}
131
132pub fn is_valid_max_apdu_length(value: u16) -> bool {
135 matches!(value, 50 | 128 | 206 | 480 | 1024 | 1476)
136}
137
138pub fn validate_max_apdu_length(value: u16) -> Result<(), Error> {
140 if is_valid_max_apdu_length(value) {
141 Ok(())
142 } else {
143 Err(Error::Encoding(format!(
144 "invalid max-APDU-length {value}; expected one of 50, 128, 206, 480, 1024, 1476"
145 )))
146 }
147}
148
149fn encode_max_apdu(value: u16) -> Result<u8, Error> {
151 validate_max_apdu_length(value)?;
152 match value {
153 50 => Ok(0),
154 128 => Ok(1),
155 206 => Ok(2),
156 480 => Ok(3),
157 1024 => Ok(4),
158 1476 => Ok(5),
159 _ => unreachable!("validated max-APDU-length"),
160 }
161}
162
163fn decode_max_apdu(value: u8) -> Result<u16, Error> {
165 let idx = (value & 0x0F) as usize;
166 if idx < MAX_APDU_DECODE.len() {
167 Ok(MAX_APDU_DECODE[idx])
168 } else {
169 Err(Error::decoding(
170 1,
171 format!("reserved max-APDU-length field value {idx}"),
172 ))
173 }
174}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct ConfirmedRequest {
183 pub segmented: bool,
186 pub more_follows: bool,
188 pub segmented_response_accepted: bool,
190 pub max_segments: Option<u8>,
194 pub max_apdu_length: u16,
196 pub invoke_id: u8,
198 pub sequence_number: Option<u8>,
201 pub proposed_window_size: Option<u8>,
204 pub service_choice: ConfirmedServiceChoice,
206 pub service_request: Bytes,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct UnconfirmedRequest {
213 pub service_choice: UnconfirmedServiceChoice,
215 pub service_request: Bytes,
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct SimpleAck {
222 pub invoke_id: u8,
224 pub service_choice: ConfirmedServiceChoice,
226}
227
228#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct ComplexAck {
231 pub segmented: bool,
233 pub more_follows: bool,
235 pub invoke_id: u8,
237 pub sequence_number: Option<u8>,
240 pub proposed_window_size: Option<u8>,
243 pub service_choice: ConfirmedServiceChoice,
245 pub service_ack: Bytes,
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
251pub struct SegmentAck {
252 pub negative_ack: bool,
255 pub sent_by_server: bool,
258 pub invoke_id: u8,
260 pub sequence_number: u8,
263 pub actual_window_size: u8,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
269pub struct ErrorPdu {
270 pub invoke_id: u8,
272 pub service_choice: ConfirmedServiceChoice,
274 pub error_class: ErrorClass,
276 pub error_code: ErrorCode,
278 pub error_data: Bytes,
285}
286
287#[derive(Debug, Clone, PartialEq, Eq)]
289pub struct RejectPdu {
290 pub invoke_id: u8,
292 pub reject_reason: RejectReason,
294}
295
296#[derive(Debug, Clone, PartialEq, Eq)]
298pub struct AbortPdu {
299 pub sent_by_server: bool,
302 pub invoke_id: u8,
304 pub abort_reason: AbortReason,
306}
307
308#[derive(Debug, Clone, PartialEq, Eq)]
310pub enum Apdu {
311 ConfirmedRequest(ConfirmedRequest),
313 UnconfirmedRequest(UnconfirmedRequest),
315 SimpleAck(SimpleAck),
317 ComplexAck(ComplexAck),
319 SegmentAck(SegmentAck),
321 Error(ErrorPdu),
323 Reject(RejectPdu),
325 Abort(AbortPdu),
327}
328
329pub fn encode_apdu(buf: &mut BytesMut, apdu: &Apdu) -> Result<(), Error> {
335 match apdu {
336 Apdu::ConfirmedRequest(pdu) => encode_confirmed_request(buf, pdu),
337 Apdu::UnconfirmedRequest(pdu) => {
338 encode_unconfirmed_request(buf, pdu);
339 Ok(())
340 }
341 Apdu::SimpleAck(pdu) => {
342 encode_simple_ack(buf, pdu);
343 Ok(())
344 }
345 Apdu::ComplexAck(pdu) => encode_complex_ack(buf, pdu),
346 Apdu::SegmentAck(pdu) => encode_segment_ack(buf, pdu),
347 Apdu::Error(pdu) => encode_error(buf, pdu),
348 Apdu::Reject(pdu) => {
349 encode_reject(buf, pdu);
350 Ok(())
351 }
352 Apdu::Abort(pdu) => {
353 encode_abort(buf, pdu);
354 Ok(())
355 }
356 }
357}
358
359fn encode_confirmed_request(buf: &mut BytesMut, pdu: &ConfirmedRequest) -> Result<(), Error> {
360 let max_segments = encode_max_segments(pdu.max_segments)?;
361 let max_apdu = encode_max_apdu(pdu.max_apdu_length)?;
362
363 let mut byte0 = PduType::CONFIRMED_REQUEST.to_raw() << 4;
364 if pdu.segmented {
365 byte0 |= 0x08;
366 }
367 if pdu.more_follows {
368 byte0 |= 0x04;
369 }
370 if pdu.segmented_response_accepted {
371 byte0 |= 0x02;
372 }
373 buf.put_u8(byte0);
374
375 let byte1 = (max_segments << 4) | max_apdu;
376 buf.put_u8(byte1);
377
378 buf.put_u8(pdu.invoke_id);
379
380 if pdu.segmented {
381 buf.put_u8(pdu.sequence_number.unwrap_or(0));
382 buf.put_u8(valid_window_size(
383 "ConfirmedRequest proposed-window-size",
384 pdu.proposed_window_size.unwrap_or(1),
385 )?);
386 }
387
388 buf.put_u8(pdu.service_choice.to_raw());
389 buf.put_slice(&pdu.service_request);
390 Ok(())
391}
392
393fn encode_unconfirmed_request(buf: &mut BytesMut, pdu: &UnconfirmedRequest) {
394 buf.put_u8(PduType::UNCONFIRMED_REQUEST.to_raw() << 4);
395 buf.put_u8(pdu.service_choice.to_raw());
396 buf.put_slice(&pdu.service_request);
397}
398
399fn encode_simple_ack(buf: &mut BytesMut, pdu: &SimpleAck) {
400 buf.put_u8(PduType::SIMPLE_ACK.to_raw() << 4);
401 buf.put_u8(pdu.invoke_id);
402 buf.put_u8(pdu.service_choice.to_raw());
403}
404
405fn encode_complex_ack(buf: &mut BytesMut, pdu: &ComplexAck) -> Result<(), Error> {
406 let mut byte0 = PduType::COMPLEX_ACK.to_raw() << 4;
407 if pdu.segmented {
408 byte0 |= 0x08;
409 }
410 if pdu.more_follows {
411 byte0 |= 0x04;
412 }
413 buf.put_u8(byte0);
414
415 buf.put_u8(pdu.invoke_id);
416
417 if pdu.segmented {
418 buf.put_u8(pdu.sequence_number.unwrap_or(0));
419 buf.put_u8(valid_window_size(
420 "ComplexAck proposed-window-size",
421 pdu.proposed_window_size.unwrap_or(1),
422 )?);
423 }
424
425 buf.put_u8(pdu.service_choice.to_raw());
426 buf.put_slice(&pdu.service_ack);
427 Ok(())
428}
429
430fn encode_segment_ack(buf: &mut BytesMut, pdu: &SegmentAck) -> Result<(), Error> {
431 let mut byte0 = PduType::SEGMENT_ACK.to_raw() << 4;
432 if pdu.negative_ack {
433 byte0 |= 0x02;
434 }
435 if pdu.sent_by_server {
436 byte0 |= 0x01;
437 }
438 buf.put_u8(byte0);
439 buf.put_u8(pdu.invoke_id);
440 buf.put_u8(pdu.sequence_number);
441 buf.put_u8(valid_window_size(
442 "SegmentACK actual-window-size",
443 pdu.actual_window_size,
444 )?);
445 Ok(())
446}
447
448fn valid_window_size(field: &str, value: u8) -> Result<u8, Error> {
449 if (1..=127).contains(&value) {
450 Ok(value)
451 } else {
452 Err(Error::Encoding(format!(
453 "{field} {value} outside BACnet range 1..=127"
454 )))
455 }
456}
457
458fn encode_error(buf: &mut BytesMut, pdu: &ErrorPdu) -> Result<(), Error> {
459 let formal =
463 formal_error::decode_formal_body(pdu.service_choice, &pdu.error_data).unwrap_or(None);
464 if let Some((error_class, error_code)) = formal {
465 if error_class != pdu.error_class || error_code != pdu.error_code {
466 return Err(Error::Encoding(
467 "formal Error body disagrees with ErrorPdu class/code".into(),
468 ));
469 }
470 }
471
472 buf.put_u8(PduType::ERROR.to_raw() << 4);
473 buf.put_u8(pdu.invoke_id);
474 buf.put_u8(pdu.service_choice.to_raw());
475 if formal.is_some() {
476 buf.put_slice(&pdu.error_data);
477 } else {
478 primitives::encode_app_enumerated(buf, pdu.error_class.to_raw() as u32);
479 primitives::encode_app_enumerated(buf, pdu.error_code.to_raw() as u32);
480 if !pdu.error_data.is_empty() {
481 buf.put_slice(&pdu.error_data);
482 }
483 }
484 Ok(())
485}
486
487fn encode_reject(buf: &mut BytesMut, pdu: &RejectPdu) {
488 buf.put_u8(PduType::REJECT.to_raw() << 4);
489 buf.put_u8(pdu.invoke_id);
490 buf.put_u8(pdu.reject_reason.to_raw());
491}
492
493fn encode_abort(buf: &mut BytesMut, pdu: &AbortPdu) {
494 let mut byte0 = PduType::ABORT.to_raw() << 4;
495 if pdu.sent_by_server {
496 byte0 |= 0x01;
497 }
498 buf.put_u8(byte0);
499 buf.put_u8(pdu.invoke_id);
500 buf.put_u8(pdu.abort_reason.to_raw());
501}
502
503pub fn decode_apdu(data: Bytes) -> Result<Apdu, Error> {
509 if data.is_empty() {
510 return Err(Error::decoding(0, "APDU data is empty"));
511 }
512
513 let pdu_type_raw = (data[0] >> 4) & 0x0F;
514 let pdu_type = PduType::from_raw(pdu_type_raw);
515
516 if pdu_type == PduType::CONFIRMED_REQUEST {
517 decode_confirmed_request(data).map(Apdu::ConfirmedRequest)
518 } else if pdu_type == PduType::UNCONFIRMED_REQUEST {
519 decode_unconfirmed_request(data).map(Apdu::UnconfirmedRequest)
520 } else if pdu_type == PduType::SIMPLE_ACK {
521 decode_simple_ack(data).map(Apdu::SimpleAck)
522 } else if pdu_type == PduType::COMPLEX_ACK {
523 decode_complex_ack(data).map(Apdu::ComplexAck)
524 } else if pdu_type == PduType::SEGMENT_ACK {
525 decode_segment_ack(data).map(Apdu::SegmentAck)
526 } else if pdu_type == PduType::ERROR {
527 decode_error(data).map(Apdu::Error)
528 } else if pdu_type == PduType::REJECT {
529 decode_reject(data).map(Apdu::Reject)
530 } else if pdu_type == PduType::ABORT {
531 decode_abort(data).map(Apdu::Abort)
532 } else {
533 Err(Error::decoding(
534 0,
535 format!("unknown PDU type nibble: {:#x}", pdu_type_raw),
536 ))
537 }
538}
539
540fn decode_confirmed_request(data: Bytes) -> Result<ConfirmedRequest, Error> {
541 if data.len() < 4 {
542 return Err(Error::buffer_too_short(4, data.len()));
543 }
544
545 let byte0 = data[0];
546 let segmented = byte0 & 0x08 != 0;
547 let more_follows = byte0 & 0x04 != 0;
548 let segmented_response_accepted = byte0 & 0x02 != 0;
549
550 let byte1 = data[1];
551 let max_segments = decode_max_segments((byte1 >> 4) & 0x07);
552 let max_apdu_length = decode_max_apdu(byte1 & 0x0F)?;
553
554 let invoke_id = data[2];
555 let mut offset = 3;
556
557 let (sequence_number, proposed_window_size) = if segmented {
558 if data.len() < 6 {
559 return Err(Error::decoding(
560 offset,
561 "segmented ConfirmedRequest too short for sequence/window fields",
562 ));
563 }
564 let seq = data[offset];
565 let win = data[offset + 1];
566 offset += 2;
567 (Some(seq), Some(win))
568 } else {
569 (None, None)
570 };
571
572 if offset >= data.len() {
573 return Err(Error::decoding(
574 offset,
575 "ConfirmedRequest missing service choice",
576 ));
577 }
578 let service_choice = ConfirmedServiceChoice::from_raw(data[offset]);
579 offset += 1;
580
581 let service_request = data.slice(offset..);
582
583 Ok(ConfirmedRequest {
584 segmented,
585 more_follows,
586 segmented_response_accepted,
587 max_segments,
588 max_apdu_length,
589 invoke_id,
590 sequence_number,
591 proposed_window_size,
592 service_choice,
593 service_request,
594 })
595}
596
597fn decode_unconfirmed_request(data: Bytes) -> Result<UnconfirmedRequest, Error> {
598 if data.len() < 2 {
599 return Err(Error::buffer_too_short(2, data.len()));
600 }
601
602 let service_choice = UnconfirmedServiceChoice::from_raw(data[1]);
603 let service_request = data.slice(2..);
604
605 Ok(UnconfirmedRequest {
606 service_choice,
607 service_request,
608 })
609}
610
611fn decode_simple_ack(data: Bytes) -> Result<SimpleAck, Error> {
612 if data.len() < 3 {
613 return Err(Error::buffer_too_short(3, data.len()));
614 }
615
616 Ok(SimpleAck {
617 invoke_id: data[1],
618 service_choice: ConfirmedServiceChoice::from_raw(data[2]),
619 })
620}
621
622fn decode_complex_ack(data: Bytes) -> Result<ComplexAck, Error> {
623 if data.len() < 3 {
624 return Err(Error::buffer_too_short(3, data.len()));
625 }
626
627 let byte0 = data[0];
628 let segmented = byte0 & 0x08 != 0;
629 let more_follows = byte0 & 0x04 != 0;
630
631 let invoke_id = data[1];
632 let mut offset = 2;
633
634 let (sequence_number, proposed_window_size) = if segmented {
635 if data.len() < 5 {
636 return Err(Error::decoding(
637 offset,
638 "segmented ComplexAck too short for sequence/window fields",
639 ));
640 }
641 let seq = data[offset];
642 let win = data[offset + 1];
643 offset += 2;
644 (Some(seq), Some(win))
645 } else {
646 (None, None)
647 };
648
649 if offset >= data.len() {
650 return Err(Error::decoding(offset, "ComplexAck missing service choice"));
651 }
652 let service_choice = ConfirmedServiceChoice::from_raw(data[offset]);
653 offset += 1;
654
655 let service_ack = data.slice(offset..);
656
657 Ok(ComplexAck {
658 segmented,
659 more_follows,
660 invoke_id,
661 sequence_number,
662 proposed_window_size,
663 service_choice,
664 service_ack,
665 })
666}
667
668fn decode_segment_ack(data: Bytes) -> Result<SegmentAck, Error> {
669 if data.len() < 4 {
670 return Err(Error::buffer_too_short(4, data.len()));
671 }
672
673 let byte0 = data[0];
674 let raw_window = data[3];
675 if raw_window == 0 || raw_window > 127 {
676 return Err(Error::decoding(
677 3,
678 format!("SegmentACK actual-window-size {raw_window} outside range 1..=127"),
679 ));
680 }
681 Ok(SegmentAck {
682 negative_ack: byte0 & 0x02 != 0,
683 sent_by_server: byte0 & 0x01 != 0,
684 invoke_id: data[1],
685 sequence_number: data[2],
686 actual_window_size: raw_window,
687 })
688}
689
690fn decode_error(data: Bytes) -> Result<ErrorPdu, Error> {
691 if data.len() < 5 {
692 return Err(Error::buffer_too_short(5, data.len()));
693 }
694
695 let invoke_id = data[1];
696 let service_choice = ConfirmedServiceChoice::from_raw(data[2]);
697
698 if let Some((error_class, error_code)) =
699 formal_error::decode_formal_body(service_choice, &data[3..])?
700 {
701 return Ok(ErrorPdu {
702 invoke_id,
703 service_choice,
704 error_class,
705 error_code,
706 error_data: data.slice(3..),
707 });
708 }
709
710 let mut offset = 3;
711 let (tag, tag_end) = tags::decode_tag(&data, offset)?;
712 if tag.class != tags::TagClass::Application || tag.number != tags::app_tag::ENUMERATED {
713 return Err(Error::decoding(
714 offset,
715 "ErrorPDU error class: expected application-tagged enumerated",
716 ));
717 }
718 let class_end = tag_end
719 .checked_add(tag.length as usize)
720 .ok_or_else(|| Error::decoding(tag_end, "ErrorPDU error class length overflow"))?;
721 if class_end > data.len() {
722 return Err(Error::buffer_too_short(class_end, data.len()));
723 }
724 let error_class_raw = primitives::decode_unsigned(&data[tag_end..class_end])?;
725 let error_class_raw = u16::try_from(error_class_raw).map_err(|_| {
726 Error::out_of_range(
727 tag_end,
728 format!("ErrorPDU error class {error_class_raw} exceeds u16"),
729 )
730 })?;
731 offset = class_end;
732
733 let (tag, tag_end) = tags::decode_tag(&data, offset)?;
734 if tag.class != tags::TagClass::Application || tag.number != tags::app_tag::ENUMERATED {
735 return Err(Error::decoding(
736 offset,
737 "ErrorPDU error code: expected application-tagged enumerated",
738 ));
739 }
740 let code_end = tag_end
741 .checked_add(tag.length as usize)
742 .ok_or_else(|| Error::decoding(tag_end, "ErrorPDU error code length overflow"))?;
743 if code_end > data.len() {
744 return Err(Error::buffer_too_short(code_end, data.len()));
745 }
746 let error_code_raw = primitives::decode_unsigned(&data[tag_end..code_end])?;
747 let error_code_raw = u16::try_from(error_code_raw).map_err(|_| {
748 Error::out_of_range(
749 tag_end,
750 format!("ErrorPDU error code {error_code_raw} exceeds u16"),
751 )
752 })?;
753 offset = code_end;
754
755 let error_data = if offset < data.len() {
756 data.slice(offset..)
757 } else {
758 Bytes::new()
759 };
760
761 Ok(ErrorPdu {
762 invoke_id,
763 service_choice,
764 error_class: ErrorClass::from_raw(error_class_raw),
765 error_code: ErrorCode::from_raw(error_code_raw),
766 error_data,
767 })
768}
769
770fn decode_reject(data: Bytes) -> Result<RejectPdu, Error> {
771 if data.len() < 3 {
772 return Err(Error::buffer_too_short(3, data.len()));
773 }
774
775 Ok(RejectPdu {
776 invoke_id: data[1],
777 reject_reason: RejectReason::from_raw(data[2]),
778 })
779}
780
781fn decode_abort(data: Bytes) -> Result<AbortPdu, Error> {
782 if data.len() < 3 {
783 return Err(Error::buffer_too_short(3, data.len()));
784 }
785
786 let byte0 = data[0];
787 Ok(AbortPdu {
788 sent_by_server: byte0 & 0x01 != 0,
789 invoke_id: data[1],
790 abort_reason: AbortReason::from_raw(data[2]),
791 })
792}
793
794#[cfg(test)]
799mod change_list_error_tests;
800#[cfg(test)]
801mod structured_error_tests;
802#[cfg(test)]
803mod tests;
804#[cfg(test)]
805mod wpm_error_tests;