use crate::layer::ReceivedApdu;
use bacnet_encoding::npdu::{Npdu, NpduAddress, encode_npdu};
use bacnet_npdu::NetworkPriority;
use bacnet_transport::port::{DirectResponse, TransportProvenance};
use bacnet_types::error::Error;
use bytes::{Bytes, BytesMut};
#[cfg(test)]
#[path = "response_encode_tests.rs"]
mod encode_tests;
#[cfg(test)]
#[path = "response_budget_tests.rs"]
mod budget_tests;
#[cfg(test)]
mod size_tests {
use super::*;
use bacnet_transport::port::ReceivedNpdu;
#[test]
fn received_envelopes_and_routes_stay_small() {
let sizes = [
("ReceivedApdu", std::mem::size_of::<ReceivedApdu>(), 208),
("ReceivedNpdu", std::mem::size_of::<ReceivedNpdu>(), 160),
("ResponseRoute", std::mem::size_of::<ResponseRoute>(), 64),
];
for (name, size, limit) in sizes {
assert!(size <= limit, "{name} is {size} bytes, over {limit}");
}
}
}
#[derive(Clone, Debug)]
pub struct ResponseRoute {
provenance: TransportProvenance,
direct: Option<DirectResponse>,
router: Option<crate::router::RouterResponse>,
}
impl ResponseRoute {
pub fn new(provenance: TransportProvenance, direct: Option<DirectResponse>) -> Self {
Self {
provenance,
direct,
router: None,
}
}
pub fn unverified() -> Self {
Self::new(TransportProvenance::unverified(), None)
}
pub fn provenance(&self) -> TransportProvenance {
self.provenance.clone()
}
pub fn max_apdu_length(
&self,
apdu_limit: u16,
destination: Option<&NpduAddress>,
) -> Result<u16, Error> {
let direct = self.direct()?;
let header = self.header_len(destination)?;
let mut npdu_limit = usize::MAX;
if let Some(router) = &self.router {
npdu_limit = usize::from(router.max_npdu());
}
if let Some(direct) = direct {
npdu_limit = npdu_limit.min(direct.max_npdu_length());
}
Ok(usize::from(apdu_limit).min(npdu_limit.saturating_sub(header)) as u16)
}
fn header_len(&self, destination: Option<&NpduAddress>) -> Result<usize, Error> {
let mut scratch = [0u8; 64];
let written = match &self.router {
Some(router) => router
.plan()
.npdu(&[], false, NetworkPriority::Normal)
.encode_into(&mut scratch),
None => {
check_response_destination(destination)?;
response_npdu(&[], destination, false, NetworkPriority::Normal)
.encode_into(&mut scratch)
}
};
match written {
Ok(len) => Ok(len),
Err(_) => self
.encode(&[], destination, false, NetworkPriority::Normal)
.map(|header| header.len()),
}
}
pub fn requires_direct_response(&self) -> bool {
self.provenance.is_direct_peer() || self.direct.is_some()
}
pub fn requires_original_route(&self) -> bool {
self.router.is_some() || self.provenance.is_direct_peer() || self.direct.is_some()
}
pub fn same_origin(&self, other: &Self) -> bool {
self.provenance == other.provenance
&& match (&self.router, &other.router) {
(Some(a), Some(b)) => a.same_origin(b),
(None, None) => true,
_ => false,
}
}
pub fn encode_reply_npdu(
&self,
apdu: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> Result<BytesMut, Error> {
if self.direct()?.is_some() {
return Err(Error::Encoding(
"a direct response requires its original socket".into(),
));
}
if let Some(router) = &self.router {
router.check()?;
}
self.encode(apdu, destination, expecting_reply, priority)
}
pub(crate) fn router(&self) -> Option<&crate::router::RouterResponse> {
self.router.as_ref()
}
pub(crate) fn encode(
&self,
apdu: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> Result<BytesMut, Error> {
if let Some(router) = &self.router {
let npdu = router.plan().npdu(apdu, expecting_reply, priority);
let mut encoded = BytesMut::zeroed(npdu.encoded_len());
npdu.encode_into(&mut encoded)
.map_err(|error| Error::Encoding(error.to_string()))?;
return Ok(encoded);
}
encode_response_npdu(apdu, destination, expecting_reply, priority)
}
pub(crate) fn direct(&self) -> Result<Option<&DirectResponse>, bacnet_types::error::Error> {
match (self.provenance.direct_sc_identity(), self.direct.as_ref()) {
(Some(identity), Some(route)) if identity == route.identity() => Ok(Some(route)),
(None, None) => Ok(None),
_ => Err(bacnet_types::error::Error::Transport(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"missing or mismatched original direct response capability",
))),
}
}
}
impl ReceivedApdu {
pub fn set_response_route(&mut self, route: ResponseRoute) {
self.provenance = route.provenance;
self.direct_response = route.direct;
self.router_response = route.router;
}
pub fn response_route(&self) -> ResponseRoute {
ResponseRoute {
provenance: self.provenance.clone(),
direct: self.direct_response.clone(),
router: self.router_response.clone(),
}
}
}
pub(crate) fn encode_response_npdu(
apdu: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> Result<BytesMut, Error> {
check_response_destination(destination)?;
encode_checked_response_npdu(apdu, destination, expecting_reply, priority)
}
pub(crate) fn check_response_destination(destination: Option<&NpduAddress>) -> Result<(), Error> {
match destination {
Some(destination) => crate::layer::check_destination(
destination.network,
"pass no destination (no DNET) for a local peer",
),
None => Ok(()),
}
}
pub(crate) fn encode_checked_response_npdu(
apdu: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> Result<BytesMut, Error> {
let npdu = response_npdu(apdu, destination, expecting_reply, priority);
let mut encoded = BytesMut::zeroed(npdu.encoded_len());
if npdu.encode_into(&mut encoded).is_ok() {
return Ok(encoded);
}
let npdu = Npdu {
destination: destination.cloned(),
expecting_reply,
priority,
payload: Bytes::copy_from_slice(apdu),
..Npdu::default()
};
let mut encoded = BytesMut::new();
encode_npdu(&mut encoded, &npdu)?;
Ok(encoded)
}
fn response_npdu<'a>(
apdu: &'a [u8],
destination: Option<&'a NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> bacnet_npdu::Npdu<'a> {
let mut npdu = bacnet_npdu::Npdu::apdu(apdu);
npdu.destination = destination.map(|destination| bacnet_npdu::Address {
network: destination.network,
mac: &destination.mac_address,
});
npdu.expecting_reply = expecting_reply;
npdu.priority = priority;
npdu
}