use super::*;
use bacnet_types::constructed::{
BACnetBDTEntry, BACnetFDTEntry, BACnetHostAddress, BACnetHostNPort,
};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
const V6: [u8; 16] = [0xFE, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
fn v6() -> Ipv6Addr {
Ipv6Addr::from(V6)
}
fn host(ip: [u8; 4], port: u16) -> BACnetHostNPort {
BACnetHostNPort {
host: BACnetHostAddress::Ip(IpAddr::V4(Ipv4Addr::from(ip))),
port,
}
}
fn with(prefix: &[u8], body: &[u8], suffix: &[u8]) -> Vec<u8> {
[prefix, body, suffix].concat()
}
#[test]
fn host_address_choice_arms_have_their_own_tags() {
let name = b"\x00bbmd.example";
let cases: Vec<(Vec<u8>, BACnetHostAddress)> = vec![
(vec![0x08], BACnetHostAddress::None),
(
vec![0x1C, 192, 168, 1, 10],
BACnetHostAddress::Ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10))),
),
(
with(&[0x1D, 0x10], &V6, &[]),
BACnetHostAddress::Ip(IpAddr::V6(v6())),
),
(
with(&[0x2D, 0x0D], name, &[]),
BACnetHostAddress::Name("bbmd.example".into()),
),
];
for (bytes, value) in cases {
let mut buf = BytesMut::new();
encode_host_address(&mut buf, &value).unwrap();
assert_eq!(&buf[..], &bytes[..], "{value:?}");
assert_eq!(
decode_host_address(&bytes, 0).unwrap(),
(value, bytes.len())
);
}
}
#[test]
fn host_n_port_frames_the_host_choice_in_tag_zero() {
let name = b"\x00bbmd.example";
let cases: Vec<(Vec<u8>, BACnetHostNPort)> = vec![
(
vec![0x0E, 0x1C, 10, 0, 0, 1, 0x0F, 0x1A, 0xBA, 0xC0],
host([10, 0, 0, 1], 0xBAC0),
),
(
vec![0x0E, 0x08, 0x0F, 0x19, 0x00],
BACnetHostNPort {
host: BACnetHostAddress::None,
port: 0,
},
),
(
with(&[0x0E, 0x2D, 0x0D], name, &[0x0F, 0x1A, 0xBA, 0xC1]),
BACnetHostNPort {
host: BACnetHostAddress::Name("bbmd.example".into()),
port: 0xBAC1,
},
),
(
with(&[0x0E, 0x1D, 0x10], &V6, &[0x0F, 0x19, 80]),
BACnetHostNPort {
host: BACnetHostAddress::Ip(IpAddr::V6(v6())),
port: 80,
},
),
];
for (bytes, value) in cases {
let mut buf = BytesMut::new();
encode_host_n_port(&mut buf, &value).unwrap();
assert_eq!(&buf[..], &bytes[..], "{value:?}");
assert_eq!(decode_host_n_port(&bytes, 0).unwrap(), (value, bytes.len()));
}
assert_eq!(
BACnetHostNPort::from_socket_addr(SocketAddr::from(([10, 0, 0, 1], 0xBAC0))),
host([10, 0, 0, 1], 0xBAC0)
);
}
#[test]
fn bdt_entries_frame_the_bbmd_address_and_carry_an_optional_mask() {
let ipv4 = BACnetBDTEntry {
bbmd_address: host([10, 0, 0, 1], 0xBAC0),
broadcast_mask: Some([0xFF, 0xFF, 0xFF, 0xFF]),
};
let ipv4_bytes = [
0x0E, 0x0E, 0x1C, 10, 0, 0, 1, 0x0F, 0x1A, 0xBA, 0xC0, 0x0F, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, ];
let ipv6 = BACnetBDTEntry {
bbmd_address: BACnetHostNPort {
host: BACnetHostAddress::Ip(IpAddr::V6(v6())),
port: 0xBAC0,
},
broadcast_mask: None,
};
let ipv6_bytes = with(
&[0x0E, 0x0E, 0x1D, 0x10],
&V6,
&[0x0F, 0x1A, 0xBA, 0xC0, 0x0F],
);
for (bytes, entry) in [(&ipv4_bytes[..], &ipv4), (&ipv6_bytes[..], &ipv6)] {
let mut buf = BytesMut::new();
encode_bdt_entry(&mut buf, entry).unwrap();
assert_eq!(&buf[..], bytes);
assert_eq!(
decode_bdt_entry(bytes, 0).unwrap(),
(entry.clone(), bytes.len())
);
}
let list = [ipv6.clone(), ipv4.clone()];
let mut buf = BytesMut::new();
encode_bdt_entry_list(&mut buf, &list).unwrap();
assert_eq!(&buf[..], [&ipv6_bytes[..], &ipv4_bytes[..]].concat());
assert_eq!(decode_bdt_entry_list(&buf).unwrap(), list);
assert_eq!(decode_bdt_entry_list(&[]).unwrap(), Vec::new());
}
#[test]
fn fdt_entries_are_three_primitives() {
let ipv4 = BACnetFDTEntry {
address: SocketAddr::from(([10, 0, 0, 5], 0xBAC0)),
time_to_live: 60,
remaining_time_to_live: 85,
};
let ipv4_bytes = [
0x0D, 0x06, 10, 0, 0, 5, 0xBA, 0xC0, 0x19, 60, 0x29, 85, ];
let ipv6 = BACnetFDTEntry {
address: SocketAddr::from((v6(), 0xBAC0)),
time_to_live: 300,
remaining_time_to_live: 330,
};
let ipv6_bytes = with(
&[0x0D, 0x12],
&V6,
&[0xBA, 0xC0, 0x1A, 0x01, 0x2C, 0x2A, 0x01, 0x4A],
);
for (bytes, entry) in [(&ipv4_bytes[..], &ipv4), (&ipv6_bytes[..], &ipv6)] {
let mut buf = BytesMut::new();
encode_fdt_entry(&mut buf, entry);
assert_eq!(&buf[..], bytes);
assert_eq!(
decode_fdt_entry(bytes, 0).unwrap(),
(entry.clone(), bytes.len())
);
}
let list = [ipv4, ipv6];
let mut buf = BytesMut::new();
encode_fdt_entry_list(&mut buf, &list);
assert_eq!(&buf[..], [&ipv4_bytes[..], &ipv6_bytes[..]].concat());
assert_eq!(decode_fdt_entry_list(&buf).unwrap(), list);
assert_eq!(decode_fdt_entry_list(&[]).unwrap(), Vec::new());
}
#[test]
fn host_address_refuses_malformed_alternatives() {
for bytes in [
&[0x09, 0x00][..],
&[0x18][..],
&[0x1D, 0x05, 1, 2, 3, 4, 5][..],
&[0x1D, 0x06, 1, 2, 3, 4, 5, 6][..],
&[0x1C, 10, 0][..],
&[0x28][..],
&[0x2A, 0x00, 0xFF][..],
&[0x39, 0x00][..],
&[0x21, 0x00][..],
&[0x1E, 0x1F][..],
&[][..],
] {
assert!(
decode_host_address(bytes, 0).is_err(),
"{bytes:02X?} must not decode"
);
}
}
#[test]
fn host_n_port_refuses_malformed_sequences() {
for bytes in [
&[0x1C, 10, 0, 0, 1, 0x1A, 0xBA, 0xC0][..],
&[0x0E, 0x08, 0x19, 0x00][..],
&[0x0E, 0x08, 0x08, 0x0F, 0x19, 0x00][..],
&[0x0E, 0x0F, 0x19, 0x00][..],
&[0x0E, 0x08, 0x0F][..],
&[0x0E, 0x08, 0x0F, 0x1B, 0x01, 0x00, 0x00][..],
&[0x0E, 0x08, 0x0F, 0x21, 0x00][..],
&[0x0E, 0x08, 0x0F, 0x18][..],
] {
assert!(
decode_host_n_port(bytes, 0).is_err(),
"{bytes:02X?} must not decode"
);
}
}
#[test]
fn bdt_entry_refuses_malformed_entries() {
let inner = [0x0E, 0x1C, 10, 0, 0, 1, 0x0F, 0x1A, 0xBA, 0xC0];
for bytes in [
inner.to_vec(),
with(&[0x0E], &inner, &[]),
with(&[0x0E], &inner, &[0x1C, 0xFF, 0xFF, 0xFF, 0xFF]),
with(&[0x0E], &inner, &[0x0F, 0x1B, 0xFF, 0xFF, 0xFF]),
with(
&[0x0E],
&inner,
&with(&[0x0F, 0x1D, 0x10], &[0xFF; 16], &[]),
),
with(&[0x0E], &inner, &[0x0F, 0x1C, 0xFF]),
] {
assert!(
decode_bdt_entry(&bytes, 0).is_err(),
"{bytes:02X?} must not decode"
);
}
let mut list = with(&[0x0E], &inner, &[0x0F]);
list.push(0x1C);
assert!(decode_bdt_entry_list(&list).is_err());
}
#[test]
fn fdt_entry_refuses_malformed_entries() {
for bytes in [
&[0x0C, 10, 0, 0, 5, 0x19, 60, 0x29, 85][..],
&[0x08, 0x19, 60, 0x29, 85][..],
&[0x0D, 0x07, 10, 0, 0, 5, 0xBA, 0xC0, 0, 0x19, 60, 0x29, 85][..],
&[0x0D, 0x06, 10, 0, 0, 5, 0xBA, 0xC0, 0x1B, 1, 0, 0, 0x29, 85][..],
&[0x0D, 0x06, 10, 0, 0, 5, 0xBA, 0xC0, 0x18, 0x29, 85][..],
&[0x0D, 0x06, 10, 0, 0, 5, 0xBA, 0xC0, 0x19, 60][..],
&[0x0D, 0x06, 10, 0, 0, 5, 0xBA, 0xC0, 0x29, 85, 0x19, 60][..],
&[0x0E, 0x0F, 0x19, 60, 0x29, 85][..],
&[0x0D, 0x06, 10, 0, 0][..],
] {
assert!(
decode_fdt_entry(bytes, 0).is_err(),
"{bytes:02X?} must not decode"
);
}
let mut list = vec![0x0D, 0x06, 10, 0, 0, 5, 0xBA, 0xC0, 0x19, 60, 0x29, 85];
list.push(0x0D);
assert!(decode_fdt_entry_list(&list).is_err());
}
#[test]
fn every_truncation_and_octet_flip_decodes_or_errors_without_panicking() {
let mut bdt = BytesMut::new();
encode_bdt_entry_list(
&mut bdt,
&[
BACnetBDTEntry {
bbmd_address: host([10, 0, 0, 1], 0xBAC0),
broadcast_mask: Some([0xFF; 4]),
},
BACnetBDTEntry {
bbmd_address: BACnetHostNPort {
host: BACnetHostAddress::Name("bbmd.example".into()),
port: 0xBAC0,
},
broadcast_mask: None,
},
],
)
.unwrap();
let mut fdt = BytesMut::new();
encode_fdt_entry_list(
&mut fdt,
&[BACnetFDTEntry {
address: SocketAddr::from((v6(), 0xBAC0)),
time_to_live: 300,
remaining_time_to_live: 330,
}],
);
type Decodes = fn(&[u8]) -> bool;
let decoders: [(&[u8], Decodes); 2] = [
(&bdt, |b| decode_bdt_entry_list(b).is_ok()),
(&fdt, |b| decode_fdt_entry_list(b).is_ok()),
];
for (bytes, decodes) in decoders {
assert!(decodes(bytes));
for len in 0..bytes.len() {
decodes(&bytes[..len]);
}
for at in 0..bytes.len() {
for flip in [0x01, 0x08, 0x80, 0xFF] {
let mut mutated = bytes.to_vec();
mutated[at] ^= flip;
decodes(&mutated);
}
}
}
}