smb_server_proto/wire.rs
1//! Codec trait for on-wire structures.
2//!
3//! Every SMB message structure implements [`Wire`], providing:
4//! - `decode`: parse from a byte slice, **borrowing** variable-length data
5//! (zero-copy — no intermediate copies)
6//! - `encode`: serialize into a growable byte buffer
7//!
8//! Endianness is handled explicitly via [`endian`] helpers (`from_le_bytes`
9//! / `to_le_bytes`) rather than native casting, so the code is correct on
10//! any host architecture.
11
12use crate::error::ProtoError;
13
14/// Context threaded through encode/decode calls.
15///
16/// Carries information that affects how individual fields are interpreted:
17/// Unicode string mode, absolute frame offsets (for parity), dialect.
18#[derive(Debug, Clone, Copy)]
19pub struct Ctx {
20 /// Absolute frame offset corresponding to the start of the buffer
21 /// being decoded. Used for Unicode parity alignment.
22 pub base: usize,
23 /// Strings are UTF-16LE when set, otherwise OEM/ASCII.
24 pub unicode: bool,
25}
26
27impl Ctx {
28 /// New context with the given frame base offset and string mode.
29 pub fn new(base: usize, unicode: bool) -> Self {
30 Ctx { base, unicode }
31 }
32
33 /// True if the absolute position `abs` is odd relative to the frame base.
34 pub fn is_odd(&self, abs: usize) -> bool {
35 abs & 1 != 0
36 }
37}
38
39/// Codec trait for fixed-layout and variable-layout on-wire structures.
40///
41/// The `'de` lifetime ties decoded variable-length data (names, blobs) to
42/// the input buffer, enabling zero-copy parsing.
43pub trait Wire<'de>: Sized {
44 /// Decode one instance starting at `pos` within `src`.
45 ///
46 /// Returns the parsed value and the offset immediately past it.
47 /// Variable-length fields are borrowed from `src`.
48 fn decode(src: &'de [u8], pos: usize, ctx: &Ctx) -> Result<(Self, usize), ProtoError>;
49
50 /// Append the encoded form to `dst`.
51 fn encode(&self, dst: &mut Vec<u8>, ctx: &Ctx);
52
53 /// Number of bytes this value occupies in its encoded form.
54 fn encoded_len(&self) -> usize;
55}
56
57/// Fixed-width unsigned integer implementations.
58macro_rules! impl_wire_int {
59 ($($ty:ty),*) => {$(
60 impl<'de> Wire<'de> for $ty {
61 #[inline]
62 fn decode(src: &'de [u8], pos: usize, _ctx: &Ctx) -> Result<(Self, usize), ProtoError> {
63 let sz = std::mem::size_of::<$ty>();
64 if pos + sz > src.len() {
65 return Err(ProtoError::Overrun { need: sz, at: pos, have: src.len() });
66 }
67 let mut raw = [0u8; std::mem::size_of::<$ty>()];
68 raw.copy_from_slice(&src[pos..pos + sz]);
69 Ok((<$ty>::from_le_bytes(raw), pos + sz))
70 }
71 #[inline]
72 fn encode(&self, dst: &mut Vec<u8>, _ctx: &Ctx) {
73 dst.extend_from_slice(&self.to_le_bytes());
74 }
75 #[inline]
76 fn encoded_len(&self) -> usize { std::mem::size_of::<$ty>() }
77 }
78 )*};
79}
80
81impl_wire_int!(u8, u16, u32, u64);