1#[cfg(feature = "alloc")]
18use bacnet_types::enums::{
19 AbortReason, ConfirmedServiceChoice, ErrorClass, ErrorCode, PduType, RejectReason,
20 UnconfirmedServiceChoice,
21};
22#[cfg(feature = "alloc")]
23use bacnet_types::error::{DecodeError, DecodingKind, Error, RangeError};
24#[cfg(feature = "alloc")]
25use bytes::{BufMut, Bytes, BytesMut};
26
27#[cfg(feature = "alloc")]
28use crate::primitives;
29#[cfg(feature = "alloc")]
30use crate::tags;
31
32#[cfg(feature = "alloc")]
33mod formal_error;
34pub mod header;
35#[cfg(feature = "alloc")]
36mod remote_error;
37
38use header::{MaxApdu, MaxSegments};
39
40#[cfg(feature = "alloc")]
51pub fn validate_max_segments(value: Option<u8>) -> Result<(), Error> {
52 encode_max_segments(value).map(drop)
53}
54
55#[cfg(feature = "alloc")]
60fn encode_max_segments(value: Option<u8>) -> Result<u8, Error> {
61 match MaxSegments::from_capacity(value) {
62 Some(field) => Ok(field.bits()),
63 None => Err(RangeError::new(
64 "invalid max-segments-accepted {}; expected 2..=255 or unspecified",
65 )
66 .with_value(value.unwrap_or(0).into())
67 .into()),
68 }
69}
70
71#[cfg(feature = "alloc")]
73fn decode_max_segments(value: u8) -> Option<u8> {
74 MaxSegments::from_bits(value).count()
75}
76
77pub fn advertised_max_segments(configured: Option<u8>) -> Option<u8> {
91 match MaxSegments::from_capacity(configured)?.count() {
92 Some(255) => None,
94 decoded => decoded,
95 }
96}
97
98pub const MINIMUM_MESSAGE_SIZE: u16 = 50;
109
110#[cfg(feature = "alloc")]
118pub fn max_apdu_header_at_or_below(capacity: u32) -> Result<u16, Error> {
119 MaxApdu::at_or_below(capacity)
120 .map(MaxApdu::octets)
121 .ok_or_else(|| {
122 RangeError::new("local APDU capacity {} is below 50")
123 .with_value(capacity.into())
124 .into()
125 })
126}
127
128pub fn is_valid_max_apdu_length(value: u16) -> bool {
131 MaxApdu::from_octets(value).is_some()
132}
133
134#[cfg(feature = "alloc")]
137pub fn validate_max_apdu_length(value: u16) -> Result<(), Error> {
138 encode_max_apdu(value).map(drop)
139}
140
141#[cfg(feature = "alloc")]
143fn encode_max_apdu(value: u16) -> Result<u8, Error> {
144 match MaxApdu::from_octets(value) {
145 Some(field) => Ok(field.bits()),
146 None => Err(RangeError::new(
147 "invalid max-APDU-length {}; expected one of 50, 128, 206, 480, 1024, 1476",
148 )
149 .with_value(value.into())
150 .into()),
151 }
152}
153
154#[cfg(feature = "alloc")]
156fn decode_max_apdu(value: u8) -> Result<u16, Error> {
157 match MaxApdu::from_bits(value) {
158 Some(field) => Ok(field.octets()),
159 None => Err(DecodeError::new(
160 DecodingKind::InvalidEncoding,
161 1,
162 "reserved max-APDU-length field value {}",
163 )
164 .with_value(u64::from(value & 0x0F))
165 .into()),
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
175#[cfg(feature = "alloc")]
176pub struct ConfirmedRequest {
177 pub segmented: bool,
180 pub more_follows: bool,
182 pub segmented_response_accepted: bool,
184 pub max_segments: Option<u8>,
188 pub max_apdu_length: u16,
190 pub invoke_id: u8,
192 pub sequence_number: Option<u8>,
195 pub proposed_window_size: Option<u8>,
198 pub service_choice: ConfirmedServiceChoice,
200 pub service_request: Bytes,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
206#[cfg(feature = "alloc")]
207pub struct UnconfirmedRequest {
208 pub service_choice: UnconfirmedServiceChoice,
210 pub service_request: Bytes,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
216#[cfg(feature = "alloc")]
217pub struct SimpleAck {
218 pub invoke_id: u8,
220 pub service_choice: ConfirmedServiceChoice,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
226#[cfg(feature = "alloc")]
227pub struct ComplexAck {
228 pub segmented: bool,
230 pub more_follows: bool,
232 pub invoke_id: u8,
234 pub sequence_number: Option<u8>,
237 pub proposed_window_size: Option<u8>,
240 pub service_choice: ConfirmedServiceChoice,
242 pub service_ack: Bytes,
244}
245
246#[derive(Debug, Clone, PartialEq, Eq)]
248#[cfg(feature = "alloc")]
249pub struct SegmentAck {
250 pub negative_ack: bool,
253 pub sent_by_server: bool,
256 pub invoke_id: u8,
258 pub sequence_number: u8,
261 pub actual_window_size: u8,
263}
264
265#[derive(Debug, Clone, PartialEq, Eq)]
267#[cfg(feature = "alloc")]
268pub struct ErrorPdu {
269 pub invoke_id: u8,
271 pub service_choice: ConfirmedServiceChoice,
273 pub error_class: ErrorClass,
275 pub error_code: ErrorCode,
277 pub error_data: Bytes,
284}
285
286#[derive(Debug, Clone, PartialEq, Eq)]
288#[cfg(feature = "alloc")]
289pub struct RejectPdu {
290 pub invoke_id: u8,
292 pub reject_reason: RejectReason,
294}
295
296#[derive(Debug, Clone, PartialEq, Eq)]
298#[cfg(feature = "alloc")]
299pub struct AbortPdu {
300 pub sent_by_server: bool,
303 pub invoke_id: u8,
305 pub abort_reason: AbortReason,
307}
308
309#[derive(Debug, Clone, PartialEq, Eq)]
311#[cfg(feature = "alloc")]
312pub enum Apdu {
313 ConfirmedRequest(ConfirmedRequest),
315 UnconfirmedRequest(UnconfirmedRequest),
317 SimpleAck(SimpleAck),
319 ComplexAck(ComplexAck),
321 SegmentAck(SegmentAck),
323 Error(ErrorPdu),
325 Reject(RejectPdu),
327 Abort(AbortPdu),
329}
330
331#[cfg(feature = "alloc")]
337pub fn encode_apdu(buf: &mut BytesMut, apdu: &Apdu) -> Result<(), Error> {
338 match apdu {
339 Apdu::ConfirmedRequest(pdu) => encode_confirmed_request(buf, pdu),
340 Apdu::UnconfirmedRequest(pdu) => {
341 encode_unconfirmed_request(buf, pdu);
342 Ok(())
343 }
344 Apdu::SimpleAck(pdu) => {
345 encode_simple_ack(buf, pdu);
346 Ok(())
347 }
348 Apdu::ComplexAck(pdu) => encode_complex_ack(buf, pdu),
349 Apdu::SegmentAck(pdu) => encode_segment_ack(buf, pdu),
350 Apdu::Error(pdu) => encode_error(buf, pdu),
351 Apdu::Reject(pdu) => {
352 encode_reject(buf, pdu);
353 Ok(())
354 }
355 Apdu::Abort(pdu) => {
356 encode_abort(buf, pdu);
357 Ok(())
358 }
359 }
360}
361
362#[cfg(feature = "alloc")]
363fn encode_confirmed_request(buf: &mut BytesMut, pdu: &ConfirmedRequest) -> Result<(), Error> {
364 let max_segments = encode_max_segments(pdu.max_segments)?;
367 let max_apdu = encode_max_apdu(pdu.max_apdu_length)?;
368 let window = pdu
369 .segmented
370 .then(|| {
371 valid_window_size(
372 "ConfirmedRequest proposed-window-size {} outside BACnet range 1..=127",
373 pdu.proposed_window_size.unwrap_or(1),
374 )
375 })
376 .transpose()?;
377
378 let mut byte0 = PduType::CONFIRMED_REQUEST.to_raw() << 4;
379 if pdu.segmented {
380 byte0 |= 0x08;
381 }
382 if pdu.more_follows {
383 byte0 |= 0x04;
384 }
385 if pdu.segmented_response_accepted {
386 byte0 |= 0x02;
387 }
388 buf.put_u8(byte0);
389
390 let byte1 = (max_segments << 4) | max_apdu;
391 buf.put_u8(byte1);
392
393 buf.put_u8(pdu.invoke_id);
394
395 if let Some(window) = window {
396 buf.put_u8(pdu.sequence_number.unwrap_or(0));
397 buf.put_u8(window);
398 }
399
400 buf.put_u8(pdu.service_choice.to_raw());
401 buf.put_slice(&pdu.service_request);
402 Ok(())
403}
404
405#[cfg(feature = "alloc")]
406fn encode_unconfirmed_request(buf: &mut BytesMut, pdu: &UnconfirmedRequest) {
407 buf.put_u8(PduType::UNCONFIRMED_REQUEST.to_raw() << 4);
408 buf.put_u8(pdu.service_choice.to_raw());
409 buf.put_slice(&pdu.service_request);
410}
411
412#[cfg(feature = "alloc")]
413fn encode_simple_ack(buf: &mut BytesMut, pdu: &SimpleAck) {
414 buf.put_u8(PduType::SIMPLE_ACK.to_raw() << 4);
415 buf.put_u8(pdu.invoke_id);
416 buf.put_u8(pdu.service_choice.to_raw());
417}
418
419#[cfg(feature = "alloc")]
420fn encode_complex_ack(buf: &mut BytesMut, pdu: &ComplexAck) -> Result<(), Error> {
421 let window = pdu
423 .segmented
424 .then(|| {
425 valid_window_size(
426 "ComplexAck proposed-window-size {} outside BACnet range 1..=127",
427 pdu.proposed_window_size.unwrap_or(1),
428 )
429 })
430 .transpose()?;
431
432 let mut byte0 = PduType::COMPLEX_ACK.to_raw() << 4;
433 if pdu.segmented {
434 byte0 |= 0x08;
435 }
436 if pdu.more_follows {
437 byte0 |= 0x04;
438 }
439 buf.put_u8(byte0);
440
441 buf.put_u8(pdu.invoke_id);
442
443 if let Some(window) = window {
444 buf.put_u8(pdu.sequence_number.unwrap_or(0));
445 buf.put_u8(window);
446 }
447
448 buf.put_u8(pdu.service_choice.to_raw());
449 buf.put_slice(&pdu.service_ack);
450 Ok(())
451}
452
453#[cfg(feature = "alloc")]
454fn encode_segment_ack(buf: &mut BytesMut, pdu: &SegmentAck) -> Result<(), Error> {
455 let window = valid_window_size(
457 "SegmentACK actual-window-size {} outside BACnet range 1..=127",
458 pdu.actual_window_size,
459 )?;
460 let mut byte0 = PduType::SEGMENT_ACK.to_raw() << 4;
461 if pdu.negative_ack {
462 byte0 |= 0x02;
463 }
464 if pdu.sent_by_server {
465 byte0 |= 0x01;
466 }
467 buf.put_u8(byte0);
468 buf.put_u8(pdu.invoke_id);
469 buf.put_u8(pdu.sequence_number);
470 buf.put_u8(window);
471 Ok(())
472}
473
474#[cfg(feature = "alloc")]
477fn valid_window_size(label: &'static str, value: u8) -> Result<u8, Error> {
478 if (1..=127).contains(&value) {
479 Ok(value)
480 } else {
481 Err(RangeError::new(label).with_value(value.into()).into())
482 }
483}
484
485#[cfg(feature = "alloc")]
486fn encode_error(buf: &mut BytesMut, pdu: &ErrorPdu) -> Result<(), Error> {
487 let formal =
491 formal_error::decode_formal_body(pdu.service_choice, &pdu.error_data).unwrap_or(None);
492 if let Some((error_class, error_code)) = formal
493 && (error_class != pdu.error_class || error_code != pdu.error_code)
494 {
495 return Err(Error::encode(
496 "formal Error body disagrees with ErrorPdu class/code",
497 ));
498 }
499
500 buf.put_u8(PduType::ERROR.to_raw() << 4);
501 buf.put_u8(pdu.invoke_id);
502 buf.put_u8(pdu.service_choice.to_raw());
503 if formal.is_some() {
504 buf.put_slice(&pdu.error_data);
505 } else {
506 primitives::encode_app_enumerated(buf, u32::from(pdu.error_class.to_raw()));
507 primitives::encode_app_enumerated(buf, u32::from(pdu.error_code.to_raw()));
508 if !pdu.error_data.is_empty() {
509 buf.put_slice(&pdu.error_data);
510 }
511 }
512 Ok(())
513}
514
515#[cfg(feature = "alloc")]
516fn encode_reject(buf: &mut BytesMut, pdu: &RejectPdu) {
517 buf.put_u8(PduType::REJECT.to_raw() << 4);
518 buf.put_u8(pdu.invoke_id);
519 buf.put_u8(pdu.reject_reason.to_raw());
520}
521
522#[cfg(feature = "alloc")]
523fn encode_abort(buf: &mut BytesMut, pdu: &AbortPdu) {
524 let mut byte0 = PduType::ABORT.to_raw() << 4;
525 if pdu.sent_by_server {
526 byte0 |= 0x01;
527 }
528 buf.put_u8(byte0);
529 buf.put_u8(pdu.invoke_id);
530 buf.put_u8(pdu.abort_reason.to_raw());
531}
532
533#[cfg(feature = "alloc")]
539pub fn decode_apdu(data: Bytes) -> Result<Apdu, Error> {
540 if data.is_empty() {
541 return Err(Error::decoding(0, "APDU data is empty"));
542 }
543
544 let pdu_type_raw = (data[0] >> 4) & 0x0F;
545 let pdu_type = PduType::from_raw(pdu_type_raw);
546
547 if pdu_type == PduType::CONFIRMED_REQUEST {
548 decode_confirmed_request(data).map(Apdu::ConfirmedRequest)
549 } else if pdu_type == PduType::UNCONFIRMED_REQUEST {
550 decode_unconfirmed_request(data).map(Apdu::UnconfirmedRequest)
551 } else if pdu_type == PduType::SIMPLE_ACK {
552 decode_simple_ack(data).map(Apdu::SimpleAck)
553 } else if pdu_type == PduType::COMPLEX_ACK {
554 decode_complex_ack(data).map(Apdu::ComplexAck)
555 } else if pdu_type == PduType::SEGMENT_ACK {
556 decode_segment_ack(data).map(Apdu::SegmentAck)
557 } else if pdu_type == PduType::ERROR {
558 decode_error(data).map(Apdu::Error)
559 } else if pdu_type == PduType::REJECT {
560 decode_reject(data).map(Apdu::Reject)
561 } else if pdu_type == PduType::ABORT {
562 decode_abort(data).map(Apdu::Abort)
563 } else {
564 Err(DecodeError::new(
565 DecodingKind::InvalidEncoding,
566 0,
567 "unknown PDU type nibble: {:#x}",
568 )
569 .with_value(pdu_type_raw.into())
570 .into())
571 }
572}
573
574#[cfg(feature = "alloc")]
575fn decode_confirmed_request(data: Bytes) -> Result<ConfirmedRequest, Error> {
576 if data.len() < 4 {
577 return Err(Error::buffer_too_short(4, data.len()));
578 }
579
580 let byte0 = data[0];
581 let segmented = byte0 & 0x08 != 0;
582 let more_follows = byte0 & 0x04 != 0;
583 let segmented_response_accepted = byte0 & 0x02 != 0;
584
585 let byte1 = data[1];
586 let max_segments = decode_max_segments((byte1 >> 4) & 0x07);
587 let max_apdu_length = decode_max_apdu(byte1 & 0x0F)?;
588
589 let invoke_id = data[2];
590 let mut offset = 3;
591
592 let (sequence_number, proposed_window_size) = if segmented {
593 if data.len() < 6 {
594 return Err(Error::decoding(
595 offset,
596 "segmented ConfirmedRequest too short for sequence/window fields",
597 ));
598 }
599 let seq = data[offset];
600 let win = data[offset + 1];
601 offset += 2;
602 (Some(seq), Some(win))
603 } else {
604 (None, None)
605 };
606
607 if offset >= data.len() {
608 return Err(Error::decoding(
609 offset,
610 "ConfirmedRequest missing service choice",
611 ));
612 }
613 let service_choice = ConfirmedServiceChoice::from_raw(data[offset]);
614 offset += 1;
615
616 let service_request = data.slice(offset..);
617
618 Ok(ConfirmedRequest {
619 segmented,
620 more_follows,
621 segmented_response_accepted,
622 max_segments,
623 max_apdu_length,
624 invoke_id,
625 sequence_number,
626 proposed_window_size,
627 service_choice,
628 service_request,
629 })
630}
631
632#[cfg(feature = "alloc")]
633fn decode_unconfirmed_request(data: Bytes) -> Result<UnconfirmedRequest, Error> {
634 if data.len() < 2 {
635 return Err(Error::buffer_too_short(2, data.len()));
636 }
637
638 let service_choice = UnconfirmedServiceChoice::from_raw(data[1]);
639 let service_request = data.slice(2..);
640
641 Ok(UnconfirmedRequest {
642 service_choice,
643 service_request,
644 })
645}
646
647#[cfg(feature = "alloc")]
648fn decode_simple_ack(data: Bytes) -> Result<SimpleAck, Error> {
649 if data.len() < 3 {
650 return Err(Error::buffer_too_short(3, data.len()));
651 }
652
653 Ok(SimpleAck {
654 invoke_id: data[1],
655 service_choice: ConfirmedServiceChoice::from_raw(data[2]),
656 })
657}
658
659#[cfg(feature = "alloc")]
660fn decode_complex_ack(data: Bytes) -> Result<ComplexAck, Error> {
661 if data.len() < 3 {
662 return Err(Error::buffer_too_short(3, data.len()));
663 }
664
665 let byte0 = data[0];
666 let segmented = byte0 & 0x08 != 0;
667 let more_follows = byte0 & 0x04 != 0;
668
669 let invoke_id = data[1];
670 let mut offset = 2;
671
672 let (sequence_number, proposed_window_size) = if segmented {
673 if data.len() < 5 {
674 return Err(Error::decoding(
675 offset,
676 "segmented ComplexAck too short for sequence/window fields",
677 ));
678 }
679 let seq = data[offset];
680 let win = data[offset + 1];
681 offset += 2;
682 (Some(seq), Some(win))
683 } else {
684 (None, None)
685 };
686
687 if offset >= data.len() {
688 return Err(Error::decoding(offset, "ComplexAck missing service choice"));
689 }
690 let service_choice = ConfirmedServiceChoice::from_raw(data[offset]);
691 offset += 1;
692
693 let service_ack = data.slice(offset..);
694
695 Ok(ComplexAck {
696 segmented,
697 more_follows,
698 invoke_id,
699 sequence_number,
700 proposed_window_size,
701 service_choice,
702 service_ack,
703 })
704}
705
706#[cfg(feature = "alloc")]
707fn decode_segment_ack(data: Bytes) -> Result<SegmentAck, Error> {
708 if data.len() < 4 {
709 return Err(Error::buffer_too_short(4, data.len()));
710 }
711
712 let byte0 = data[0];
713 let raw_window = data[3];
714 if raw_window == 0 || raw_window > 127 {
715 return Err(DecodeError::new(
716 DecodingKind::InvalidEncoding,
717 3,
718 "SegmentACK actual-window-size {} outside range 1..=127",
719 )
720 .with_value(raw_window.into())
721 .into());
722 }
723 Ok(SegmentAck {
724 negative_ack: byte0 & 0x02 != 0,
725 sent_by_server: byte0 & 0x01 != 0,
726 invoke_id: data[1],
727 sequence_number: data[2],
728 actual_window_size: raw_window,
729 })
730}
731
732#[cfg(feature = "alloc")]
733fn decode_error(data: Bytes) -> Result<ErrorPdu, Error> {
734 if data.len() < 5 {
735 return Err(Error::buffer_too_short(5, data.len()));
736 }
737
738 let invoke_id = data[1];
739 let service_choice = ConfirmedServiceChoice::from_raw(data[2]);
740
741 if let Some((error_class, error_code)) =
742 formal_error::decode_formal_body(service_choice, &data[3..])?
743 {
744 return Ok(ErrorPdu {
745 invoke_id,
746 service_choice,
747 error_class,
748 error_code,
749 error_data: data.slice(3..),
750 });
751 }
752
753 let mut offset = 3;
754 let (tag, tag_end) = tags::decode_tag(&data, offset)?;
755 if tag.class != tags::TagClass::Application || tag.number != tags::app_tag::ENUMERATED {
756 return Err(Error::decoding(
757 offset,
758 "ErrorPDU error class: expected application-tagged enumerated",
759 ));
760 }
761 let class_end = tag_end
762 .checked_add(tag.length as usize)
763 .ok_or_else(|| Error::decoding(tag_end, "ErrorPDU error class length overflow"))?;
764 if class_end > data.len() {
765 return Err(Error::buffer_too_short(class_end, data.len()));
766 }
767 let error_class_raw = primitives::decode_unsigned(&data[tag_end..class_end])?;
768 let error_class_raw = u16::try_from(error_class_raw).map_err(|_| {
769 DecodeError::new(
770 DecodingKind::OutOfRange,
771 tag_end,
772 "ErrorPDU error class {} exceeds u16",
773 )
774 .with_value(error_class_raw)
775 })?;
776 offset = class_end;
777
778 let (tag, tag_end) = tags::decode_tag(&data, offset)?;
779 if tag.class != tags::TagClass::Application || tag.number != tags::app_tag::ENUMERATED {
780 return Err(Error::decoding(
781 offset,
782 "ErrorPDU error code: expected application-tagged enumerated",
783 ));
784 }
785 let code_end = tag_end
786 .checked_add(tag.length as usize)
787 .ok_or_else(|| Error::decoding(tag_end, "ErrorPDU error code length overflow"))?;
788 if code_end > data.len() {
789 return Err(Error::buffer_too_short(code_end, data.len()));
790 }
791 let error_code_raw = primitives::decode_unsigned(&data[tag_end..code_end])?;
792 let error_code_raw = u16::try_from(error_code_raw).map_err(|_| {
793 DecodeError::new(
794 DecodingKind::OutOfRange,
795 tag_end,
796 "ErrorPDU error code {} exceeds u16",
797 )
798 .with_value(error_code_raw)
799 })?;
800 offset = code_end;
801
802 let error_data = if offset < data.len() {
803 data.slice(offset..)
804 } else {
805 Bytes::new()
806 };
807
808 Ok(ErrorPdu {
809 invoke_id,
810 service_choice,
811 error_class: ErrorClass::from_raw(error_class_raw),
812 error_code: ErrorCode::from_raw(error_code_raw),
813 error_data,
814 })
815}
816
817#[cfg(feature = "alloc")]
818fn decode_reject(data: Bytes) -> Result<RejectPdu, Error> {
819 if data.len() < 3 {
820 return Err(Error::buffer_too_short(3, data.len()));
821 }
822
823 Ok(RejectPdu {
824 invoke_id: data[1],
825 reject_reason: RejectReason::from_raw(data[2]),
826 })
827}
828
829#[cfg(feature = "alloc")]
830fn decode_abort(data: Bytes) -> Result<AbortPdu, Error> {
831 if data.len() < 3 {
832 return Err(Error::buffer_too_short(3, data.len()));
833 }
834
835 let byte0 = data[0];
836 Ok(AbortPdu {
837 sent_by_server: byte0 & 0x01 != 0,
838 invoke_id: data[1],
839 abort_reason: AbortReason::from_raw(data[2]),
840 })
841}
842
843#[cfg(all(test, feature = "alloc"))]
848mod change_list_error_tests;
849#[cfg(all(test, feature = "alloc"))]
850mod encode_refusal_tests;
851#[cfg(all(test, feature = "alloc"))]
852mod remote_error_tests;
853#[cfg(all(test, feature = "alloc"))]
854mod structured_error_tests;
855#[cfg(all(test, feature = "alloc"))]
856mod tests;
857#[cfg(all(test, feature = "alloc"))]
858mod wpm_error_tests;