Skip to main content

bacnet_objects/device/
audit_recipient.rs

1//! Device-owned recipient state and the synchronous runtime mutation capability.
2use super::*;
3use bacnet_types::constructed::{BACnetAddress, BACnetRecipient};
4use std::sync::Weak;
5
6/// Actual remote requester context, supplied only after service authorization.
7#[doc(hidden)]
8#[derive(Clone)]
9pub struct AuditWriteSource {
10    pub device: BACnetRecipient,
11    pub invoke_id: u8,
12}
13
14/// Installed target runtime. Implementations must prepare both deliveries before
15/// changing `current`, then commit and register their owned worker atomically.
16#[doc(hidden)]
17pub trait AuditRecipientChangeSink: Send + Sync {
18    fn change(
19        &self,
20        current: &mut BACnetRecipient,
21        new: BACnetRecipient,
22        source: Option<&AuditWriteSource>,
23        clock: Option<ClockFrame>,
24    ) -> Result<(), Error>;
25    fn is_active(&self) -> bool;
26}
27
28#[derive(Default)]
29pub(super) struct RecipientState {
30    value: Option<BACnetRecipient>,
31    sink: Option<Weak<dyn AuditRecipientChangeSink>>,
32}
33
34fn property_error(code: ErrorCode) -> Error {
35    Error::Protocol {
36        class: ErrorClass::PROPERTY,
37        code,
38    }
39}
40
41/// A scoped operation capability for the built-in Device. It deliberately does
42/// not expose a mutable Device reference or permit replacing its installed state.
43#[doc(hidden)]
44pub struct DeviceAuthority<'a>(pub(super) &'a mut DeviceObject);
45
46impl DeviceAuthority<'_> {
47    pub fn object_identifier(&self) -> ObjectIdentifier {
48        self.0.oid
49    }
50    pub fn set_services_supported(&mut self, services: &[ServiceSupported]) {
51        self.0.set_services_supported(services);
52    }
53    pub fn set_object_list(&mut self, oids: Vec<ObjectIdentifier>) {
54        self.0.set_object_list(oids);
55    }
56    pub fn set_system_status(&mut self, status: DeviceStatus) {
57        self.0.set_system_status(status);
58    }
59    pub fn set_database_revision(&mut self, revision: u32) {
60        self.0.set_database_revision(revision);
61    }
62    pub fn set_backup_and_restore_state(&mut self, state: BackupAndRestoreState) {
63        self.0.set_backup_and_restore_state(state);
64    }
65    pub fn set_last_restore_time(&mut self, time: BACnetTimeStamp) {
66        self.0.set_last_restore_time(time);
67    }
68    pub fn set_configuration_files(&mut self, files: Vec<ObjectIdentifier>) {
69        self.0.set_configuration_files(files);
70    }
71    pub fn write_property(
72        &mut self,
73        property: PropertyIdentifier,
74        index: Option<u32>,
75        value: PropertyValue,
76        priority: Option<u8>,
77    ) -> Result<(), Error> {
78        self.0.write_property(property, index, value, priority)
79    }
80    pub fn provision_audit_recipient(&mut self, recipient: BACnetRecipient) -> Result<(), Error> {
81        self.0.provision_audit_recipient(recipient)
82    }
83    pub fn provisioned_audit_recipient(&self) -> Option<&BACnetRecipient> {
84        self.0.recipient.value.as_ref()
85    }
86    pub fn validate_audit_recipient_installation(&self) -> Result<(), Error> {
87        if self.0.recipient.value.is_none()
88            || self
89                .0
90                .recipient
91                .sink
92                .as_ref()
93                .and_then(Weak::upgrade)
94                .is_some()
95        {
96            return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
97        }
98        Ok(())
99    }
100    pub fn install_audit_recipient(
101        &mut self,
102        sink: &Arc<dyn AuditRecipientChangeSink>,
103    ) -> Result<(), Error> {
104        self.validate_audit_recipient_installation()?;
105        self.0.recipient.sink = Some(Arc::downgrade(sink));
106        Ok(())
107    }
108    pub fn uninstall_audit_recipient(&mut self, sink: &Arc<dyn AuditRecipientChangeSink>) {
109        if !sink.is_active()
110            && self
111                .0
112                .recipient
113                .sink
114                .as_ref()
115                .and_then(Weak::upgrade)
116                .is_some_and(|current| Arc::ptr_eq(&current, sink))
117        {
118            self.0.recipient.sink = None;
119        }
120    }
121    pub fn write_audit_recipient(
122        &mut self,
123        index: Option<u32>,
124        value: PropertyValue,
125        priority: Option<u8>,
126        source: Option<&AuditWriteSource>,
127    ) -> Result<(), Error> {
128        self.0.write_audit_recipient(index, value, priority, source)
129    }
130}
131
132impl DeviceObject {
133    /// Provision the initial target Audit recipient before installing a server
134    /// runtime. This alone does not expose the network property. Active changes
135    /// must use WriteProperty so both old and new recipients are notified.
136    pub fn provision_audit_recipient(&mut self, recipient: BACnetRecipient) -> Result<(), Error> {
137        validate_recipient(&recipient)?;
138        if self
139            .recipient
140            .sink
141            .as_ref()
142            .and_then(Weak::upgrade)
143            .is_some()
144        {
145            return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
146        }
147        self.recipient.value = Some(recipient);
148        Ok(())
149    }
150
151    pub(super) fn audit_recipient_present(&self) -> bool {
152        self.recipient.value.is_some()
153            && self
154                .recipient
155                .sink
156                .as_ref()
157                .and_then(Weak::upgrade)
158                .is_some()
159    }
160
161    pub(super) fn read_audit_recipient(&self, index: Option<u32>) -> Result<PropertyValue, Error> {
162        if !self.audit_recipient_present() {
163            return Err(property_error(ErrorCode::UNKNOWN_PROPERTY));
164        }
165        if index.is_some() {
166            return Err(property_error(ErrorCode::PROPERTY_IS_NOT_AN_ARRAY));
167        }
168        let mut bytes = bytes::BytesMut::new();
169        bacnet_encoding::constructed::encode_recipient(
170            &mut bytes,
171            self.recipient.value.as_ref().expect("present"),
172        )?;
173        Ok(PropertyValue::ApplicationData(bytes.to_vec()))
174    }
175
176    pub(super) fn write_audit_recipient(
177        &mut self,
178        index: Option<u32>,
179        value: PropertyValue,
180        priority: Option<u8>,
181        source: Option<&AuditWriteSource>,
182    ) -> Result<(), Error> {
183        let sink = self
184            .recipient
185            .sink
186            .as_ref()
187            .and_then(Weak::upgrade)
188            .ok_or_else(|| property_error(ErrorCode::UNKNOWN_PROPERTY))?;
189        if !sink.is_active() {
190            return Err(property_error(ErrorCode::WRITE_ACCESS_DENIED));
191        }
192        if index.is_some() {
193            return Err(property_error(ErrorCode::PROPERTY_IS_NOT_AN_ARRAY));
194        }
195        if priority.is_some_and(|p| !(1..=16).contains(&p)) {
196            return Err(Error::Protocol {
197                class: ErrorClass::SERVICES,
198                code: ErrorCode::PARAMETER_OUT_OF_RANGE,
199            });
200        }
201        if value == PropertyValue::Null {
202            return Ok(());
203        }
204        let PropertyValue::ApplicationData(bytes) = value else {
205            return Err(property_error(ErrorCode::INVALID_DATA_TYPE));
206        };
207        let (new, end) = bacnet_encoding::constructed::decode_recipient(&bytes, 0)
208            .map_err(|_| property_error(ErrorCode::INVALID_DATA_ENCODING))?;
209        if end != bytes.len() {
210            return Err(property_error(ErrorCode::INVALID_DATA_ENCODING));
211        }
212        validate_recipient(&new)?;
213        let clock = self.clock_frame();
214        let current = self.recipient.value.as_mut().expect("installed recipient");
215        if *current == new {
216            return Ok(());
217        }
218        sink.change(current, new, source, clock)
219    }
220}
221
222/// A recipient the property can hold: a concrete Device, or an address whose
223/// MAC is within the configured-recipient bound. Provisioning checks the MAC
224/// here because nothing decodes it on the way in (#1124); a write already has.
225fn validate_recipient(recipient: &BACnetRecipient) -> Result<(), Error> {
226    match recipient {
227        BACnetRecipient::Device(oid)
228            if oid.object_type() != ObjectType::DEVICE
229                || oid.instance_number() == ObjectIdentifier::MAX_INSTANCE =>
230        {
231            Err(property_error(ErrorCode::INVALID_DATA_ENCODING))
232        }
233        BACnetRecipient::Address(address)
234            if address.mac_address.len() > BACnetAddress::MAX_MAC_LEN =>
235        {
236            Err(property_error(ErrorCode::INVALID_DATA_ENCODING))
237        }
238        _ => Ok(()),
239    }
240}