#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use bacnet_types::constructed::{
BACnetBDTEntry, BACnetFDTEntry, BACnetHostAddress, BACnetHostNPort,
};
use bacnet_types::error::Error;
use bytes::BytesMut;
use super::tagged::{
contents, decode_ctx_character_string, decode_ctx_fixed, decode_ctx_unsigned,
decode_optional_ctx, expect_closing, expect_opening, misplaced_tag,
};
use super::MAX_FRAMED_ITEMS;
use crate::primitives;
use crate::tags::{self, TagClass};
pub fn encode_host_address(buf: &mut BytesMut, host: &BACnetHostAddress) -> Result<(), Error> {
match host {
BACnetHostAddress::None => tags::encode_tag(buf, 0, TagClass::Context, 0),
BACnetHostAddress::Ip(IpAddr::V4(ip)) => {
primitives::encode_ctx_octet_string(buf, 1, &ip.octets())
}
BACnetHostAddress::Ip(IpAddr::V6(ip)) => {
primitives::encode_ctx_octet_string(buf, 1, &ip.octets())
}
BACnetHostAddress::Name(name) => {
let mut encoded = BytesMut::new();
primitives::encode_ctx_character_string(&mut encoded, 2, name)?;
buf.extend_from_slice(&encoded);
}
}
Ok(())
}
pub fn decode_host_address(
data: &[u8],
offset: usize,
) -> Result<(BACnetHostAddress, usize), Error> {
let what = "BACnetHostAddress";
let (tag, start) = tags::decode_tag(data, offset)?;
if tag.is_context(0) {
if tag.length != 0 {
return Err(Error::decoding(
offset,
format!("{what} none [0]: NULL has {} contents octets", tag.length),
));
}
return Ok((BACnetHostAddress::None, start));
}
if tag.is_context(1) {
let (octets, end) = contents(data, start, tag.length)?;
let ip = match *octets {
[a, b, c, d] => IpAddr::V4(Ipv4Addr::new(a, b, c, d)),
_ => match <[u8; 16]>::try_from(octets) {
Ok(v6) => IpAddr::V6(Ipv6Addr::from(v6)),
Err(_) => {
return Err(Error::decoding(
offset,
format!(
"{what} ip-address [1]: {} octets, expected 4 or 16",
octets.len()
),
))
}
},
};
return Ok((BACnetHostAddress::Ip(ip), end));
}
if tag.is_context(2) {
let (name, end) = decode_ctx_character_string(data, offset, 2, what)?;
return Ok((BACnetHostAddress::Name(name), end));
}
Err(misplaced_tag(
data,
&tag,
None,
offset,
format!("{what}: expected none [0], ip-address [1] or name [2]"),
))
}
pub fn encode_host_n_port(buf: &mut BytesMut, value: &BACnetHostNPort) -> Result<(), Error> {
let mut encoded = BytesMut::new();
tags::encode_opening_tag(&mut encoded, 0);
encode_host_address(&mut encoded, &value.host)?;
tags::encode_closing_tag(&mut encoded, 0);
primitives::encode_ctx_unsigned(&mut encoded, 1, value.port.into());
buf.extend_from_slice(&encoded);
Ok(())
}
pub fn decode_host_n_port(data: &[u8], offset: usize) -> Result<(BACnetHostNPort, usize), Error> {
let what = "BACnetHostNPort";
let pos = expect_opening(data, offset, 0, what)?;
let (host, pos) = decode_host_address(data, pos)?;
let pos = expect_closing(data, pos, 0, what)?;
let (port, pos) = decode_ctx_unsigned::<u16>(data, pos, 1, what)?;
Ok((BACnetHostNPort { host, port }, pos))
}
pub fn encode_bdt_entry(buf: &mut BytesMut, entry: &BACnetBDTEntry) -> Result<(), Error> {
let mut encoded = BytesMut::new();
tags::encode_opening_tag(&mut encoded, 0);
encode_host_n_port(&mut encoded, &entry.bbmd_address)?;
tags::encode_closing_tag(&mut encoded, 0);
if let Some(mask) = &entry.broadcast_mask {
primitives::encode_ctx_octet_string(&mut encoded, 1, mask);
}
buf.extend_from_slice(&encoded);
Ok(())
}
pub fn decode_bdt_entry(data: &[u8], offset: usize) -> Result<(BACnetBDTEntry, usize), Error> {
let what = "BACnetBDTEntry";
let pos = expect_opening(data, offset, 0, what)?;
let (bbmd_address, pos) = decode_host_n_port(data, pos)?;
let pos = expect_closing(data, pos, 0, what)?;
let (mask, pos) = decode_optional_ctx(data, pos, 1, what, |data, at, tag, what| {
decode_ctx_fixed(data, at, tag, 4, "broadcast mask", what)
})?;
let broadcast_mask = mask.map(|octets| [octets[0], octets[1], octets[2], octets[3]]);
Ok((
BACnetBDTEntry {
bbmd_address,
broadcast_mask,
},
pos,
))
}
pub fn encode_fdt_entry(buf: &mut BytesMut, entry: &BACnetFDTEntry) {
let mut address = [0u8; 18];
let len = match entry.address {
SocketAddr::V4(v4) => {
address[..4].copy_from_slice(&v4.ip().octets());
address[4..6].copy_from_slice(&v4.port().to_be_bytes());
6
}
SocketAddr::V6(v6) => {
address[..16].copy_from_slice(&v6.ip().octets());
address[16..].copy_from_slice(&v6.port().to_be_bytes());
18
}
};
primitives::encode_ctx_octet_string(buf, 0, &address[..len]);
primitives::encode_ctx_unsigned(buf, 1, entry.time_to_live.into());
primitives::encode_ctx_unsigned(buf, 2, entry.remaining_time_to_live.into());
}
pub fn decode_fdt_entry(data: &[u8], offset: usize) -> Result<(BACnetFDTEntry, usize), Error> {
let what = "BACnetFDTEntry";
let (tag, start) = tags::decode_tag(data, offset)?;
if !tag.is_context(0) {
return Err(misplaced_tag(
data,
&tag,
Some(0),
offset,
format!("{what}: expected bacnetip-address [0]"),
));
}
let (octets, pos) = contents(data, start, tag.length)?;
let address = match octets.len() {
6 => SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]),
u16::from_be_bytes([octets[4], octets[5]]),
)),
18 => {
let mut ip = [0u8; 16];
ip.copy_from_slice(&octets[..16]);
SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::from(ip),
u16::from_be_bytes([octets[16], octets[17]]),
0,
0,
))
}
other => {
return Err(Error::decoding(
offset,
format!("{what} bacnetip-address [0]: {other} octets, expected 6 or 18"),
))
}
};
let (time_to_live, pos) = decode_ctx_unsigned::<u16>(data, pos, 1, what)?;
let (remaining_time_to_live, pos) = decode_ctx_unsigned::<u16>(data, pos, 2, what)?;
Ok((
BACnetFDTEntry {
address,
time_to_live,
remaining_time_to_live,
},
pos,
))
}
pub fn encode_bdt_entry_list(buf: &mut BytesMut, entries: &[BACnetBDTEntry]) -> Result<(), Error> {
let mut encoded = BytesMut::new();
for entry in entries {
encode_bdt_entry(&mut encoded, entry)?;
}
buf.extend_from_slice(&encoded);
Ok(())
}
pub fn encode_fdt_entry_list(buf: &mut BytesMut, entries: &[BACnetFDTEntry]) {
for entry in entries {
encode_fdt_entry(buf, entry);
}
}
pub fn decode_bdt_entry_list(data: &[u8]) -> Result<Vec<BACnetBDTEntry>, Error> {
decode_list(data, "BACnetBDTEntry list", decode_bdt_entry)
}
pub fn decode_fdt_entry_list(data: &[u8]) -> Result<Vec<BACnetFDTEntry>, Error> {
decode_list(data, "BACnetFDTEntry list", decode_fdt_entry)
}
fn decode_list<T>(
data: &[u8],
what: &str,
decode: impl Fn(&[u8], usize) -> Result<(T, usize), Error>,
) -> Result<Vec<T>, Error> {
let mut values = Vec::new();
let mut offset = 0;
while offset < data.len() {
if values.len() == MAX_FRAMED_ITEMS {
return Err(Error::decoding(
offset,
format!("{what}: more than {MAX_FRAMED_ITEMS} entries"),
));
}
let (value, end) = decode(data, offset)?;
values.push(value);
offset = end;
}
Ok(values)
}