use std::str;
use serde::de::{Error as _, Unexpected};
use winnow::stream::{Location, Stream};
use zbus_utils::{object_path, signature};
use super::{
Input,
byte_order::{BigEndian, ByteOrder, LittleEndian},
};
use crate::{Error, MaxDepthExceeded, Result, message::EndianSig};
pub(crate) fn validate(
body: &[u8],
signature: &[u8],
endian_sig: EndianSig,
fds: u32,
) -> Result<()> {
if signature.is_empty() {
return match body.is_empty() {
true => Ok(()),
false => Err(Error::ExcessData),
};
}
match endian_sig {
EndianSig::Little => values::<LittleEndian>(body, signature, fds),
EndianSig::Big => values::<BigEndian>(body, signature, fds),
}
.map_err(|invalid| invalid.into_error(body, endian_sig))
}
fn values<O: ByteOrder>(body: &[u8], mut signature: &[u8], fds: u32) -> Check<()> {
let input = &mut Input::new(body);
while !signature.is_empty() {
value::<O>(input, &mut signature, 0, fds)?;
}
if input.eof_offset() == 0 {
Ok(())
} else {
Err(Invalid::ExcessData)
}
}
#[inline(always)]
fn value<O: ByteOrder>(
input: &mut Input<'_>,
signature: &mut &[u8],
depth: u8,
fds: u32,
) -> Check<()> {
let Some((&code, rest)) = signature.split_first() else {
return Err(Invalid::Signature);
};
match code {
b'v' => {
let depth = deeper(depth)?;
match signature_value(input)? {
[code] if *code != b'v' => basic::<O>(input, *code, fds)?,
signature => variant::<O>(input, signature, depth, fds)?,
}
}
b'a' if let Some(size) = rest.first().and_then(|code| fixed_size(*code)) => {
deeper(depth)?;
fixed_array::<O>(input, size)?;
*signature = &rest[1..];
return Ok(());
}
b'a' | b'(' => return container::<O>(input, signature, depth, fds),
_ => basic::<O>(input, code, fds)?,
}
*signature = rest;
Ok(())
}
#[inline(always)]
fn basic<O: ByteOrder>(input: &mut Input<'_>, code: u8, fds: u32) -> Check<()> {
match code {
b'y' => bytes(input, 1).map(|_| ()),
b'b' => match u32::<O>(input)? {
0 | 1 => Ok(()),
value => Err(Invalid::Bool(value)),
},
b'n' | b'q' => fixed(input, 2),
b'i' | b'u' => fixed(input, 4),
b'h' => match u32::<O>(input)? < fds {
true => Ok(()),
false => Err(Invalid::Fd),
},
b'x' | b't' | b'd' => fixed(input, 8),
b's' => {
let string = string::<O>(input)?;
if is_ascii_without_nul(string) || is_str(string) {
return Ok(());
}
let start = input.current_token_start() - string.len() - 1;
Err(Invalid::Str(start as u32))
}
b'o' => {
let path = string::<O>(input)?;
object_path::validate(path).map_err(|_| Invalid::ObjectPath)
}
b'g' => signature_value(input).map(|_| ()),
_ => Err(Invalid::Signature),
}
}
fn container<O: ByteOrder>(
input: &mut Input<'_>,
signature: &mut &[u8],
depth: u8,
fds: u32,
) -> Check<()> {
let Some((&code, rest)) = signature.split_first() else {
return Err(Invalid::Signature);
};
*signature = rest;
match code {
b'a' => array::<O>(input, signature, depth, fds),
b'(' => structure::<O>(input, signature, deeper(depth)?, fds),
_ => Err(Invalid::Signature),
}
}
fn array<O: ByteOrder>(
input: &mut Input<'_>,
signature: &mut &[u8],
depth: u8,
fds: u32,
) -> Check<()> {
let len = u32::<O>(input)?;
if len > MAX_ARRAY_LEN {
return Err(Invalid::ArrayLen(len));
}
let element_len = match signature.first() {
Some(code) if is_basic(*code) => 1,
_ => complete_type_len(signature)?,
};
let (element, rest) = signature.split_at(element_len);
*signature = rest;
align(input, alignment(element[0]))?;
let end = input.current_token_start() + len as usize;
let depth = match element[0] {
b'h' => deeper(depth)?,
_ if len == 0 => depth,
_ => deeper(depth)?,
};
match element.split_first() {
Some((b'{', entry)) => {
while input.current_token_start() < end {
dict_entry::<O>(input, entry, deeper(depth)?, fds)?;
}
}
_ => {
while input.current_token_start() < end {
value::<O>(input, &mut { element }, depth, fds)?;
}
}
}
match input.current_token_start() == end {
true => Ok(()),
false => Err(Invalid::OutOfBounds),
}
}
#[inline(always)]
fn fixed_array<O: ByteOrder>(input: &mut Input<'_>, size: u32) -> Check<()> {
let len = u32::<O>(input)?;
if len > MAX_ARRAY_LEN || len & (size - 1) != 0 {
return Err(Invalid::ArrayLen(len));
}
align(input, size as usize)?;
bytes(input, len as usize).map(|_| ())
}
fn structure<O: ByteOrder>(
input: &mut Input<'_>,
signature: &mut &[u8],
depth: u8,
fds: u32,
) -> Check<()> {
align(input, 8)?;
loop {
match signature.split_first() {
Some((b')', rest)) => {
*signature = rest;
return Ok(());
}
Some(_) => value::<O>(input, signature, depth, fds)?,
None => return Err(Invalid::Signature),
}
}
}
#[inline(always)]
fn dict_entry<O: ByteOrder>(input: &mut Input<'_>, entry: &[u8], depth: u8, fds: u32) -> Check<()> {
let Some((&key, mut value_signature)) = entry.split_first() else {
return Err(Invalid::Signature);
};
align(input, 8)?;
basic::<O>(input, key, fds)?;
value::<O>(input, &mut value_signature, depth, fds)?;
match value_signature {
b"}" => Ok(()),
_ => Err(Invalid::Signature),
}
}
fn variant<O: ByteOrder>(
input: &mut Input<'_>,
mut signature: &[u8],
depth: u8,
fds: u32,
) -> Check<()> {
match signature {
[] => return Err(Invalid::Signature),
[_] | [b'a', _] => (),
_ if complete_type_len(signature)? != signature.len() => return Err(Invalid::Signature),
_ => (),
}
let max = usize::from(MAX_DEPTH) + 1;
if usize::from(depth) + signature.len() > max
&& usize::from(depth) + type_depth(signature)?.0 > max
{
return Err(Invalid::Depth);
}
value::<O>(input, &mut signature, depth, fds)
}
#[inline(always)]
fn deeper(depth: u8) -> Check<u8> {
match depth < MAX_DEPTH {
true => Ok(depth + 1),
false => Err(Invalid::Depth),
}
}
#[inline(always)]
fn signature_value<'i>(input: &mut Input<'i>) -> Check<&'i [u8]> {
let len = bytes(input, 1)?[0];
let signature = terminated(input, len.into())?;
match signature {
[code] | [b'a', code] if is_basic(*code) => Ok(signature),
_ => complex_signature(signature).map(|_| signature),
}
}
fn complex_signature(signature: &[u8]) -> Check<()> {
signature::validate(signature).map_err(|_| Invalid::Signature)
}
#[inline(always)]
fn string<'i, O: ByteOrder>(input: &mut Input<'i>) -> Check<&'i [u8]> {
let len = u32::<O>(input)?;
terminated(input, len as usize)
}
#[inline(always)]
fn terminated<'i>(input: &mut Input<'i>, len: usize) -> Check<&'i [u8]> {
if input.eof_offset() <= len {
return Err(Invalid::OutOfBounds);
}
let (end, bytes) = input
.next_slice(len + 1)
.split_last()
.expect("`len + 1` bytes");
match end {
0 => Ok(bytes),
byte => Err(Invalid::StringEnd(*byte)),
}
}
#[inline(always)]
fn u32<O: ByteOrder>(input: &mut Input<'_>) -> Check<u32> {
align(input, 4)?;
let bytes = bytes(input, 4)?;
Ok(O::u32_from_bytes(
bytes.try_into().expect("`bytes` returns 4 bytes"),
))
}
#[inline(always)]
fn fixed(input: &mut Input<'_>, size: usize) -> Check<()> {
align(input, size)?;
bytes(input, size).map(|_| ())
}
#[inline(always)]
fn align(input: &mut Input<'_>, alignment: usize) -> Check<()> {
let len = input.current_token_start().wrapping_neg() & (alignment - 1);
if len == 0 {
return Ok(());
}
match bytes(input, len)?.iter().find(|byte| **byte != 0) {
Some(byte) => Err(Invalid::PaddingNot0(*byte)),
None => Ok(()),
}
}
#[inline(always)]
fn bytes<'i>(input: &mut Input<'i>, len: usize) -> Check<&'i [u8]> {
if input.eof_offset() < len {
return Err(Invalid::OutOfBounds);
}
Ok(input.next_slice(len))
}
#[inline(always)]
fn is_ascii_without_nul(bytes: &[u8]) -> bool {
const ONES: u64 = u64::from_ne_bytes([0x01; 8]);
const TOPS: u64 = u64::from_ne_bytes([0x80; 8]);
let tops_of = |word: u64| word.wrapping_sub(ONES) | word;
let len = bytes.len();
let u32_at = |i: usize| u64::from(u32::from_ne_bytes(bytes[i..i + 4].try_into().unwrap()));
let tops = match len {
0 => return true,
1..=3 => tops_of(
u64::from(bytes[0])
| u64::from(bytes[len / 2]) << 8
| u64::from(bytes[len - 1]) << 16
| ONES << 24,
),
4..=8 => tops_of(u32_at(0) | u32_at(len - 4) << 32),
_ => {
let (words, _) = bytes.as_chunks::<8>();
let last = bytes.last_chunk::<8>().expect("more than 8 bytes");
words
.iter()
.fold(tops_of(u64::from_ne_bytes(*last)), |tops, word| {
tops | tops_of(u64::from_ne_bytes(*word))
})
}
};
tops & TOPS == 0
}
fn is_str(string: &[u8]) -> bool {
!string.contains(&0) && str::from_utf8(string).is_ok()
}
fn is_basic(code: u8) -> bool {
match code {
b'y' | b'b' | b'n' | b'q' | b'i' | b'u' | b'x' | b't' | b'd' | b's' | b'o' | b'g'
| b'v' => true,
b'h' => cfg!(unix),
_ => false,
}
}
fn type_depth(signature: &[u8]) -> Check<(usize, usize)> {
match signature.first() {
Some(b'a') => {
let (depth, len) = type_depth(&signature[1..])?;
Ok((depth + 1, len + 1))
}
Some(b'(' | b'{') => {
let (mut depth, mut len) = (0, 1);
loop {
match signature.get(len) {
Some(b')' | b'}') => return Ok((depth + 1, len + 1)),
Some(_) => {
let (child_depth, child_len) = type_depth(&signature[len..])?;
depth = depth.max(child_depth);
len += child_len;
}
None => return Err(Invalid::Signature),
}
}
}
Some(_) => Ok((1, 1)),
None => Err(Invalid::Signature),
}
}
fn complete_type_len(signature: &[u8]) -> Check<usize> {
match signature.first() {
Some(b'a') => Ok(1 + complete_type_len(&signature[1..])?),
Some(b'(' | b'{') => {
let mut len = 1;
loop {
match signature.get(len) {
Some(b')' | b'}') => return Ok(len + 1),
Some(_) => len += complete_type_len(&signature[len..])?,
None => return Err(Invalid::Signature),
}
}
}
Some(_) => Ok(1),
None => Err(Invalid::Signature),
}
}
fn alignment(code: u8) -> usize {
match code {
b'y' | b'g' | b'v' => 1,
b'n' | b'q' => 2,
b'x' | b't' | b'd' | b'(' | b'{' => 8,
_ => 4,
}
}
fn fixed_size(code: u8) -> Option<u32> {
match code {
b'y' => Some(1),
b'n' | b'q' => Some(2),
b'i' | b'u' => Some(4),
b'x' | b't' | b'd' => Some(8),
_ => None,
}
}
type Check<T> = std::result::Result<T, Invalid>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Invalid {
OutOfBounds,
ExcessData,
PaddingNot0(u8),
Bool(u32),
StringEnd(u8),
Str(u32),
ObjectPath,
Signature,
Fd,
ArrayLen(u32),
Depth,
}
impl Invalid {
#[cold]
fn into_error(self, body: &[u8], endian_sig: EndianSig) -> Error {
match self {
Self::OutOfBounds => Error::OutOfBounds,
Self::ExcessData => Error::ExcessData,
Self::PaddingNot0(byte) => Error::PaddingNot0(byte),
Self::Bool(value) => {
Error::invalid_value(Unexpected::Unsigned(value.into()), &"0 or 1")
}
Self::StringEnd(byte) => Error::invalid_value(
Unexpected::Unsigned(byte.into()),
&"the 0 that ends a string",
),
Self::Str(start) => {
let start = start as usize;
let len: [u8; 4] = body[start - 4..start].try_into().expect("4 bytes");
let len = match endian_sig {
EndianSig::Little => LittleEndian::u32_from_bytes(len),
EndianSig::Big => BigEndian::u32_from_bytes(len),
};
match str::from_utf8(&body[start..start + len as usize]) {
Ok(_) => {
Error::invalid_value(Unexpected::Char('\0'), &"a string without a 0 in it")
}
Err(e) => Error::Utf8(e),
}
}
Self::ObjectPath => Error::InvalidObjectPath,
Self::Signature => Error::SignatureParse(signature::Error::InvalidSignature),
Self::Fd => Error::UnknownFd,
Self::ArrayLen(len) if len > MAX_ARRAY_LEN => Error::ExcessData,
Self::ArrayLen(len) => {
Error::invalid_length(len as usize, &"a multiple of the size of the elements")
}
Self::Depth => Error::MaxDepthExceeded(MaxDepthExceeded::Container),
}
}
}
const MAX_DEPTH: u8 = 64;
const MAX_ARRAY_LEN: u32 = 64 * 1024 * 1024;
const _: () = assert!(size_of::<Check<&[u8]>>() <= 16);
#[cfg(test)]
mod tests {
use std::collections::HashMap;
#[cfg(unix)]
use std::{io::stdout, os::fd::AsFd};
use serde::de::IgnoredAny;
use test_log::test;
use super::{is_ascii_without_nul, validate};
#[cfg(unix)]
use crate::Fd;
use crate::{
Error, Message, ObjectPath, Signature, Structure, Variant,
message::{Body, EndianSig},
wire::{
BE, LE,
serialized::{Context, Data},
},
};
#[test]
fn built_bodies() {
for msg in messages() {
msg.body().validate().unwrap();
}
}
#[test]
fn changed_bytes() {
for msg in messages() {
let body = msg.body();
let signature = body.signature().to_string();
let endian_sig = msg.primary_header().endian_sig();
let fds = msg.header().unix_fds().unwrap_or(0);
let mut bytes = body.data().bytes().to_vec();
for i in 0..bytes.len() {
let byte = bytes[i];
for value in [0, 1, 2, 3, 4, 7, 8, b'a', b'v', b'(', b'm', 0x80, 0xff] {
bytes[i] = value;
if validate(&bytes, signature.as_bytes(), endian_sig, fds).is_err() {
continue;
}
match deserialize(&bytes, &body) {
Ok(()) => (),
Err(e) if e == crate::wire::dbus::maybe_not_in_dbus() => (),
Err(e) => panic!("{bytes:?} of `{signature}`: {e}"),
}
}
bytes[i] = byte;
}
}
}
#[test]
fn basic_values() {
assert!(matches!(check(b"b", &[2, 0, 0, 0]), Err(Error::Failure(_))));
check(b"b", &[1, 0, 0, 0]).unwrap();
check(b"yq", &[1, 0, 2, 0]).unwrap();
assert_eq!(check(b"yq", &[1, 7, 2, 0]), Err(Error::PaddingNot0(7)));
assert_eq!(check(b"u", &[1, 0, 0]), Err(Error::OutOfBounds));
assert_eq!(check(b"y", &[1, 0]), Err(Error::ExcessData));
assert_eq!(check(b"", &[0]), Err(Error::ExcessData));
check(b"", &[]).unwrap();
}
#[test]
fn strings() {
check(b"s", &[2, 0, 0, 0, b'h', b'i', 0]).unwrap();
assert!(matches!(
check(b"s", &[2, 0, 0, 0, b'h', 0xff, 0]),
Err(Error::Utf8(_))
));
assert!(matches!(
check(b"s", &[2, 0, 0, 0, b'h', 0, 0]),
Err(Error::Failure(_))
));
assert!(matches!(
check(b"s", &[2, 0, 0, 0, b'h', b'i', 1]),
Err(Error::Failure(_))
));
assert_eq!(
check(b"s", &[3, 0, 0, 0, b'h', b'i', 0]),
Err(Error::OutOfBounds)
);
assert_eq!(check(b"s", &[u8::MAX; 4]), Err(Error::OutOfBounds));
check(b"o", &[2, 0, 0, 0, b'/', b'a', 0]).unwrap();
assert_eq!(
check(b"o", &[2, 0, 0, 0, b'a', b'/', 0]),
Err(Error::InvalidObjectPath)
);
check(b"g", &[5, b'a', b'{', b's', b'v', b'}', 0]).unwrap();
assert!(matches!(
check(b"g", &[2, b'a', b'{', 0]),
Err(Error::SignatureParse(_))
));
}
#[test]
fn ascii() {
for len in 0..=33 {
let mut bytes = vec![b'a'; len];
assert!(is_ascii_without_nul(&bytes));
for i in 0..len {
for byte in [0, 1, 0x7f, 0x80, 0xff] {
bytes[i] = byte;
let ascii = byte != 0 && byte.is_ascii();
assert_eq!(is_ascii_without_nul(&bytes), ascii, "{bytes:?}");
if i > 0 {
bytes[i - 1] = 0;
assert!(!is_ascii_without_nul(&bytes), "{bytes:?}");
bytes[i - 1] = b'a';
}
}
bytes[i] = b'a';
}
}
}
#[test]
fn variants() {
check(b"v", &[1, b'y', 0, 7]).unwrap();
check(b"v", &[4, b'(', b'y', b'y', b')', 0, 0, 0, 1, 2]).unwrap();
assert!(matches!(
check(b"v", &[2, b'y', b'y', 0, 1, 2]),
Err(Error::SignatureParse(_))
));
assert!(matches!(
check(b"v", &[0, 0]),
Err(Error::SignatureParse(_))
));
for depth in [64, 65] {
let mut bytes = [1, b'v', 0].repeat(depth - 1);
bytes.extend([1, b'y', 0, 7]);
let checked = check(b"v", &bytes);
let data = Data::new(&bytes[..], Context::new(LE, 0));
let deserialized = data.deserialize_for_dynamic_signature::<_, Variant<'_>>("v");
assert_eq!(checked.is_ok(), deserialized.is_ok(), "{checked:?}");
assert_eq!(checked.is_ok(), depth == 64, "{checked:?}");
}
}
#[test]
fn depth() {
for (dicts, valid) in [(21, true), (22, false)] {
let mut bytes = vec![];
nested_dicts(&mut bytes, dicts);
let checked = check_and_deserialize(b"a{sv}", &bytes);
assert_eq!(checked.is_ok(), valid, "{dicts} dicts: {checked:?}");
}
let mut bytes = vec![];
let mut variant_signature = vec![18];
variant_signature.extend([b'a'; 17]);
variant_signature.extend([b'y', 0]);
nested_arrays(&mut bytes, 16, 1, &|bytes| {
bytes.extend(&variant_signature);
nested_arrays(bytes, 17, 1, &|bytes| bytes.push(7));
});
let mut signature = vec![b'a'; 16];
signature.push(b'v');
check_and_deserialize(&signature, &bytes).unwrap();
for (nested, valid) in [(32, true), (33, false)] {
let mut bytes = vec![nested + 1];
bytes.extend([b'a'; 33].iter().take(nested.into()));
bytes.extend([b'y', 0]);
nested_arrays(&mut bytes, 1, 4, &|_| ());
let checked = check_and_deserialize(b"v", &bytes);
assert_eq!(checked.is_ok(), valid, "{nested} arrays: {checked:?}");
let mut bytes = vec![2 * nested + 1];
bytes.extend([b'('; 33].iter().take(nested.into()));
bytes.push(b'y');
bytes.extend([b')'; 33].iter().take(nested.into()));
bytes.push(0);
pad(&mut bytes, 8);
bytes.push(7);
let checked = check_and_deserialize(b"v", &bytes);
assert_eq!(checked.is_ok(), valid, "{nested} structures: {checked:?}");
}
let max = Err(Error::MaxDepthExceeded(crate::MaxDepthExceeded::Container));
for (signature, alignment, elements, variants) in [
(&b"ay"[..], 1, &[7][..], 63),
(b"as", 4, &[], 63),
(b"as", 4, &[1, 0, 0, 0, b'7', 0], 63),
(b"ab", 4, &[1, 0, 0, 0], 63),
(b"ab", 4, &[], 63),
(b"aas", 4, &[], 62),
(b"a(yy)", 8, &[], 62),
] {
for (variants, expected) in [(variants, Ok(())), (variants + 1, max.clone())] {
let mut bytes = [1, b'v', 0].repeat(variants - 1);
bytes.push(signature.len() as u8);
bytes.extend(signature);
bytes.push(0);
nested_arrays(&mut bytes, 1, alignment, &|bytes| bytes.extend(elements));
let checked = check_and_deserialize(b"v", &bytes);
assert_eq!(
checked, expected,
"{variants} variants of {signature:?}, {elements:?}"
);
}
}
let mut arrays = vec![
(&b"y"[..], 1, &[7][..], false),
(b"y", 1, &[], false),
(b"b", 4, &[1, 0, 0, 0], false),
(b"b", 4, &[], true),
(b"s", 4, &[], true),
];
if cfg!(unix) {
arrays.push((b"h", 4, &[], false));
}
for (element, alignment, elements, valid) in arrays {
for structures in [1, 2] {
let mut signature = b"a{s".repeat(31);
signature.extend(b"(".repeat(structures));
signature.push(b'a');
signature.extend(element);
signature.extend(b")".repeat(structures));
signature.extend(b"}".repeat(31));
let mut bytes = vec![];
dicts_of_dicts(&mut bytes, 31, &|bytes| {
pad(bytes, 8);
nested_arrays(bytes, 1, alignment, &|bytes| bytes.extend(elements));
});
let expected = match valid || structures == 1 {
true => Ok(()),
false => max.clone(),
};
let checked = check_and_deserialize(&signature, &bytes);
assert_eq!(checked, expected, "{structures}, {element:?}, {elements:?}");
}
}
}
#[cfg(unix)]
#[test]
fn fds() {
assert_eq!(validate(&[1, 0, 0, 0], b"h", EndianSig::Little, 2), Ok(()));
assert_eq!(
validate(&[2, 0, 0, 0], b"h", EndianSig::Little, 2),
Err(Error::UnknownFd)
);
assert_eq!(
validate(&[4, 0, 0, 0, 0, 0, 0, 0], b"ah", EndianSig::Little, 1),
Ok(())
);
assert_eq!(
validate(&[4, 0, 0, 0, 1, 0, 0, 0], b"ah", EndianSig::Little, 1),
Err(Error::UnknownFd)
);
}
#[test]
fn arrays() {
check(b"ay", &[0, 0, 0, 0]).unwrap();
check(b"ay", &[3, 0, 0, 0, 1, 2, 3]).unwrap();
assert_eq!(
check(b"ay", &[4, 0, 0, 0, 1, 2, 3]),
Err(Error::OutOfBounds)
);
assert!(matches!(
check(b"au", &[5, 0, 0, 0, 0, 0, 0, 0, 0]),
Err(Error::Failure(_))
));
assert!(matches!(
check(b"ab", &[4, 0, 0, 0, 2, 0, 0, 0]),
Err(Error::Failure(_))
));
check(b"ax", &[0, 0, 0, 0, 0, 0, 0, 0]).unwrap();
assert_eq!(check(b"ax", &[0, 0, 0, 0]), Err(Error::OutOfBounds));
assert_eq!(
check(b"ax", &[0, 0, 0, 0, 0, 1, 0, 0]),
Err(Error::PaddingNot0(1))
);
assert_eq!(
check(b"as", &[6, 0, 0, 0, 2, 0, 0, 0, b'h', b'i', 0]),
Err(Error::OutOfBounds)
);
assert_eq!(check(b"ay", &[1, 0, 0, 4]), Err(Error::ExcessData));
check(
b"a{yy}",
&[10, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 3, 4],
)
.unwrap();
assert_eq!(
check(
b"a{yy}",
&[10, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 5, 0, 0, 0, 0, 3, 4]
),
Err(Error::PaddingNot0(5))
);
}
fn nested_dicts(bytes: &mut Vec<u8>, dicts: usize) {
nested_arrays(bytes, 1, 8, &|bytes| {
bytes.extend([1, 0, 0, 0, b'k', 0]);
match dicts {
1 => bytes.extend([1, b'y', 0, 7]),
_ => {
bytes.extend([5, b'a', b'{', b's', b'v', b'}', 0]);
nested_dicts(bytes, dicts - 1);
}
}
});
}
fn dicts_of_dicts(bytes: &mut Vec<u8>, dicts: usize, last: &dyn Fn(&mut Vec<u8>)) {
nested_arrays(bytes, 1, 8, &|bytes| {
bytes.extend([1, 0, 0, 0, b'k', 0]);
match dicts {
1 => last(bytes),
_ => dicts_of_dicts(bytes, dicts - 1, last),
}
});
}
fn nested_arrays(bytes: &mut Vec<u8>, n: usize, alignment: usize, last: &dyn Fn(&mut Vec<u8>)) {
if n == 0 {
return last(bytes);
}
pad(bytes, 4);
let len = bytes.len();
bytes.extend([0; 4]);
pad(bytes, if n == 1 { alignment } else { 4 });
let start = bytes.len();
nested_arrays(bytes, n - 1, alignment, last);
let elements_len = u32::try_from(bytes.len() - start).unwrap();
bytes[len..len + 4].copy_from_slice(&elements_len.to_le_bytes());
}
fn pad(bytes: &mut Vec<u8>, alignment: usize) {
bytes.resize(bytes.len().next_multiple_of(alignment), 0);
}
fn check(signature: &[u8], bytes: &[u8]) -> crate::Result<()> {
validate(bytes, signature, EndianSig::Little, 0)
}
fn check_and_deserialize(signature: &[u8], bytes: &[u8]) -> crate::Result<()> {
let checked = check(signature, bytes);
let data = Data::new(bytes, Context::new(LE, 0));
let deserialized = data.deserialize_for_signature::<_, IgnoredAny>(signature);
assert_eq!(
matches!(deserialized, Ok((_, len)) if len == bytes.len()),
checked.is_ok(),
"`{}`: {checked:?}, deserialized: {deserialized:?}",
String::from_utf8_lossy(signature),
);
checked
}
fn deserialize(bytes: &[u8], body: &Body) -> crate::Result<()> {
let ctxt = body.data().context();
#[cfg(unix)]
let data =
Data::new_borrowed_fds(bytes, ctxt, body.data().fds().iter().map(|fd| fd.as_fd()));
#[cfg(not(unix))]
let data = Data::new(bytes, ctxt);
let signature = Signature::try_from(format!("({})", body.signature()).as_str())?;
data.deserialize_for_dynamic_signature::<_, Structure<'_>>(&signature)
.map(|_| ())
}
fn messages() -> Vec<Message> {
let mut messages = vec![];
for endian in [LE, BE] {
let msg = || Message::method_call("/org/zbus/Test", "Test").endian(endian);
let dict = HashMap::from([
("y", Variant::from(7u8)),
("s", Variant::from("seven")),
("v", Variant::Variant(Box::new(Variant::from(7u64)))),
("as", Variant::from(vec!["se", "ven"])),
("(yx)", Variant::from((7u8, 7i64))),
("ay", Variant::from(vec![7u8; 7])),
]);
messages.extend([
msg().build(&()).unwrap(),
msg().build(&7u8).unwrap(),
msg()
.build(&(
7u8,
true,
7i16,
7u16,
7i32,
7u32,
7i64,
7u64,
7.0f64,
"seven",
ObjectPath::try_from("/org/zbus/Seven").unwrap(),
Signature::try_from("a{sv}(yx)").unwrap(),
))
.unwrap(),
msg().build(&(vec![7u8; 7], Vec::<u8>::new())).unwrap(),
msg().build(&(vec![true, false], vec![7.0f64])).unwrap(),
msg()
.build(&(7u8, Vec::<(u8, u64)>::new(), vec![(7u8, 7u64)]))
.unwrap(),
msg()
.build(&vec![vec!["se", "ven"], vec![], vec!["7"]])
.unwrap(),
msg().build(&(7u8, dict)).unwrap(),
msg()
.build(&HashMap::from([(7u8, (7i32, "seven"))]))
.unwrap(),
msg().build(&((7u8,), (7u64, (7u8, "seven")))).unwrap(),
]);
#[cfg(unix)]
messages.push(msg().build(&(7u8, Fd::from(&stdout()))).unwrap());
}
messages
}
}