use std::{
borrow::Cow,
collections::{HashMap, HashSet},
};
use test_log::test;
use super::parse;
use crate::{
Error, Signature, Variant,
message::{Fields, Header, Message, PrimaryHeader, Type, header::MIN_MESSAGE_SIZE},
names::{BusName, UniqueName, WellKnownName},
wire::{
BE, Endian, LE,
serialized::{Context, Data},
to_bytes,
},
};
#[test]
fn same_as_decoding_a_header() {
for bytes in headers() {
parse(&bytes).unwrap();
assert_same(&bytes);
let len = header_len(&bytes);
let mut changed = bytes.clone();
for i in 0..len {
for value in VALUES {
changed[i] = value;
assert_same(&changed);
}
changed[i] = bytes[i];
}
for len in 0..len {
assert_same(&bytes[..len]);
}
}
}
#[test]
fn fields_of_another_type() {
let bytes = raw_header(&[3, 1, b'o', 0, 2, 0, 0, 0, b'/', b'a', 0]);
assert_eq!(parse(&bytes).unwrap_err(), Error::InvalidField);
}
#[test]
fn unknown_fields() {
let bytes = raw_header(&[10, 1, b's', 0, 1, 0, 0, 0, b'a', 0]);
assert_eq!(parse(&bytes).unwrap_err(), Error::InvalidField);
}
#[test]
fn ends_of_strings() {
let bytes = raw_header(&[3, 1, b's', 0, 4, 0, 0, 0, b'P', b'i', b'n', b'g', 0]);
assert_eq!(header(&bytes).member().unwrap(), "Ping");
for (i, byte) in [(3, b'x'), (12, b'y')] {
let mut bytes = bytes.clone();
bytes[MIN_MESSAGE_SIZE + i] = byte;
assert_eq!(parse(&bytes).unwrap_err(), Error::InvalidField, "{bytes:?}");
}
}
#[test]
fn repeated_fields() {
let mut fields = vec![
3, 1, b's', 0, 4, 0, 0, 0, b'P', b'i', b'n', b'g', 0, 0, 0, 0,
];
fields.extend([3, 1, b's', 0, 4, 0, 0, 0, b'P', b'o', b'n', b'g', 0]);
let bytes = raw_header(&fields);
assert_eq!(parse(&bytes).unwrap_err(), Error::InvalidField);
fields[8] = b'0';
let bytes = raw_header(&fields);
assert!(matches!(parse(&bytes).unwrap_err(), Error::InvalidName(_)));
}
#[test]
fn padding() {
let mut fields = vec![
3, 1, b's', 0, 4, 0, 0, 0, b'P', b'i', b'n', b'g', 0, 0, 0, 0,
];
fields.extend([3, 1, b's', 0, 4, 0, 0, 0, b'P', b'o', b'n', b'g', 0]);
fields[14] = 7;
let bytes = raw_header(&fields);
assert_eq!(parse(&bytes).unwrap_err(), Error::PaddingNot0(7));
}
#[test]
fn destinations() {
for (destination, unique) in [
(":1.42", true),
("org.freedesktop.DBus", true),
("org.zbus.Test", false),
] {
let msg = Message::method_call("/", "Ping")
.destination(destination)
.build(&())
.unwrap();
let header = header(msg.data());
match header.destination().unwrap() {
BusName::Unique(name) if unique => assert_eq!(name, destination),
BusName::WellKnown(name) if !unique => assert_eq!(name, destination),
name => panic!("{destination} parsed as {name:?}"),
}
}
}
#[test]
fn primary_header() {
let mut bytes = raw_header(&[]);
assert_eq!(header(&bytes).message_type(), Type::MethodCall);
bytes[2] = 0x9;
bytes[3] = 2;
let primary = header(&bytes).into_primary();
assert_eq!(primary.flags().bits(), 0x9);
assert_eq!(primary.protocol_version(), 2);
bytes[1] = 5;
assert_eq!(parse(&bytes).unwrap_err(), Error::InvalidField);
bytes[1] = 1;
bytes[8..12].copy_from_slice(&[0; 4]);
assert_eq!(parse(&bytes).unwrap_err(), Error::InvalidSerial);
bytes[0] = b'x';
assert_eq!(parse(&bytes).unwrap_err(), Error::IncorrectEndian);
assert_eq!(parse(&[]).unwrap_err(), Error::OutOfBounds);
}
#[test]
fn fields_past_the_end() {
let mut bytes = raw_header(&[3, 1, b's', 0, 4, 0, 0, 0, b'P', b'i', b'n', b'g', 0]);
bytes.pop();
assert_eq!(parse(&bytes).unwrap_err(), Error::OutOfBounds);
let bytes = raw_header(&[3, 1, b's', 0, 5, 0, 0, 0, b'P', b'i', b'n', b'g', 0]);
assert_eq!(parse(&bytes).unwrap_err(), Error::OutOfBounds);
}
fn assert_same(bytes: &[u8]) {
let parsed = parse(bytes);
let endian = match bytes.first() {
Some(b'B') => BE,
_ => LE,
};
let data = Data::new(bytes, Context::new(endian, 0));
let decoded = data.deserialize::<Header<'_>>();
if bytes.len() >= MIN_MESSAGE_SIZE {
let fields_len = endian.read_u32(&bytes[MIN_MESSAGE_SIZE - 4..MIN_MESSAGE_SIZE]);
if MIN_MESSAGE_SIZE + fields_len as usize > bytes.len() {
assert!(parsed.is_err(), "{bytes:?}: {parsed:?}");
return;
}
}
match (parsed, decoded) {
(Ok((primary, len, quick_fields)), Ok((decoded, decoded_len))) => {
assert_eq!(len, decoded_len);
assert!(canonical_fields(bytes, endian), "{bytes:?}");
let fields = unsafe { quick_fields.fields(bytes) };
assert_eq_headers(&Header::new(primary, fields), &decoded, bytes);
}
(Err(_), Err(_)) => (),
(Err(Error::InvalidField), Ok(_)) if !canonical_fields(bytes, endian) => (),
(Ok(_), Err(e)) if e == crate::wire::dbus::maybe_not_in_dbus() => (),
(parsed, decoded) => panic!("{bytes:?}: parsed: {parsed:?}, decoded: {decoded:?}"),
}
}
fn canonical_fields(bytes: &[u8], endian: Endian) -> bool {
let end = MIN_MESSAGE_SIZE + endian.read_u32(&bytes[MIN_MESSAGE_SIZE - 4..]) as usize;
let byte = |i: usize| bytes.get(i).copied();
let mut codes = HashSet::new();
let mut field = MIN_MESSAGE_SIZE;
while field < end {
let value = field + 4;
let value_end = match byte(field + 2) {
Some(b'u') => value + 4,
Some(b'g') => match byte(value) {
Some(len) => value + 1 + len as usize + 1,
None => return false,
},
_ => match bytes.get(value..value + 4) {
Some(len) => value + 4 + endian.read_u32(len) as usize + 1,
None => return false,
},
};
let ends_with_0 = byte(field + 3) == Some(0)
&& (byte(field + 2) == Some(b'u') || byte(value_end - 1) == Some(0));
if !codes.insert(byte(field)) || !ends_with_0 {
return false;
}
field = value_end.next_multiple_of(8);
}
true
}
fn assert_eq_headers(parsed: &Header<'_>, decoded: &Header<'_>, bytes: &[u8]) {
let (primary, decoded_primary) = (parsed.primary(), decoded.primary());
assert_eq!(
primary.endian_sig(),
decoded_primary.endian_sig(),
"{bytes:?}"
);
assert_eq!(primary.msg_type(), decoded_primary.msg_type(), "{bytes:?}");
assert_eq!(primary.flags(), decoded_primary.flags(), "{bytes:?}");
assert_eq!(
primary.protocol_version(),
decoded_primary.protocol_version(),
"{bytes:?}"
);
assert_eq!(primary.body_len(), decoded_primary.body_len(), "{bytes:?}");
assert_eq!(
primary.serial_num(),
decoded_primary.serial_num(),
"{bytes:?}"
);
assert_eq!(parsed.path(), decoded.path(), "{bytes:?}");
assert_eq!(parsed.interface(), decoded.interface(), "{bytes:?}");
assert_eq!(parsed.member(), decoded.member(), "{bytes:?}");
assert_eq!(parsed.error_name(), decoded.error_name(), "{bytes:?}");
assert_eq!(parsed.reply_serial(), decoded.reply_serial(), "{bytes:?}");
assert_eq!(parsed.destination(), decoded.destination(), "{bytes:?}");
assert_eq!(parsed.sender(), decoded.sender(), "{bytes:?}");
assert_eq!(parsed.signature(), decoded.signature(), "{bytes:?}");
assert_eq!(parsed.unix_fds(), decoded.unix_fds(), "{bytes:?}");
}
fn header(bytes: &[u8]) -> Header<'_> {
let (primary, _, quick_fields) = parse(bytes).unwrap();
let fields = unsafe { quick_fields.fields(bytes) };
let fields = Fields {
path: fields.path.map(|path| path.to_owned()),
interface: fields.interface.map(|name| name.to_owned()),
member: fields.member.map(|name| name.to_owned()),
error_name: fields.error_name.map(|name| name.to_owned()),
reply_serial: fields.reply_serial,
destination: fields.destination.map(|name| name.to_owned()),
sender: fields.sender.map(|name| name.to_owned()),
signature: Cow::Owned(fields.signature.into_owned()),
unix_fds: fields.unix_fds,
};
Header::new(primary, fields)
}
fn header_len(bytes: &[u8]) -> usize {
let (_, len, _) = parse(bytes).unwrap();
len
}
fn raw_header(fields: &[u8]) -> Vec<u8> {
let mut bytes = vec![b'l', 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0];
bytes.extend(u32::try_from(fields.len()).unwrap().to_le_bytes());
bytes.extend(fields);
bytes
}
fn headers() -> Vec<Vec<u8>> {
let mut headers = vec![];
for endian in [Endian::Little, Endian::Big] {
let call = Message::method_call("/org/zbus/Test", "Ping")
.destination("org.zbus.Test")
.interface("org.zbus.Test")
.sender(":1.42")
.endian(endian)
.build(&(42u32, "Testtest"))
.unwrap();
let call_header = call.header();
let signal = Message::signal("/org/zbus/Test", "org.zbus.Test", "PropertiesChanged")
.sender(":1.42")
.endian(endian)
.build(&(
"org.zbus.Test",
HashMap::from([("Answer", Variant::from(42u32))]),
vec!["Question"],
))
.unwrap();
let method_return = Message::method_return(&call_header)
.sender("org.freedesktop.DBus")
.build(&())
.unwrap();
let error = Message::error(&call_header, "org.zbus.Error")
.build(&"kaboom")
.unwrap();
for msg in [&call, &signal, &method_return, &error] {
headers.push(msg.data().bytes().to_vec());
}
let fields = Fields {
path: Some("/org/zbus/Test".try_into().unwrap()),
interface: Some("org.zbus.Test".try_into().unwrap()),
member: Some("Ping".try_into().unwrap()),
error_name: Some("org.zbus.Error".try_into().unwrap()),
reply_serial: Some(7.try_into().unwrap()),
destination: Some(BusName::WellKnown(
WellKnownName::try_from("org.zbus.Test").unwrap(),
)),
sender: Some(UniqueName::try_from(":1.42").unwrap()),
signature: Cow::Owned(Signature::try_from("a{sv}(is)").unwrap()),
unix_fds: Some(1),
};
let mut primary = PrimaryHeader::new(Type::MethodCall, 0);
primary.set_endian_sig(endian.into());
let header = Header::new(primary, fields);
let ctxt = Context::new(endian, 0);
headers.push(to_bytes(ctxt, &header).unwrap().bytes().to_vec());
}
headers
}
const VALUES: [u8; 38] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0x7f, 0x80, 0xff, b'B', b'l', b'o', b's', b'g', b'u', b'v',
b'y', b'a', b'(', b')', b'{', b'}', b'm', b'h', b'.', b'/', b':', b'-', b'_', b'0', b'A', b'z',
b' ',
];