Skip to content

Commit 4289089

Browse files
authored
143 binrw structs ut refactor macros (#152)
- Fix typos & bugs in `smb-msg` structs - Migrate to use macros for test generation on `smb-msg` - Split to server/client features in `smb-dtyp` (WIP)
1 parent 16abcf3 commit 4289089

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1535
-2044
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This project is the first rust implementation of
99
the protocol that powers Windows file sharing and remote services.
1010
The project is designed to be used as a crate, but also includes a CLI tool for basic operations.
1111

12-
While most current implementations are mostly bindings to C libraries (such as libsmb2, samba, or windows' own libraries), this project is a full implementation in Rust, with no dependencies on C libraries!
12+
While most current implementations are mostly bindings to C libraries (such as libsmb2, samba, or windows' own libraries), this project is a full implementation in Rust, with no _direct_ dependencies on C libraries.
1313

1414
## Getting started
1515

@@ -23,7 +23,8 @@ Check out the `info` and the `copy` sub-commands for more information.
2323

2424
## Features
2525

26-
- ✅ SMB 2.X & 3.X support.
26+
- ✅ All SMB 2.X & 3.X dialects support.
27+
- ✅ Wire message parsing is fully safe, using the `binrw` crate.
2728
- ✅ Async (`tokio`), Multi-threaded, or Single-threaded client.
2829
- ✅ Compression & Encryption support.
2930
- ✅ Transport using SMB over TCP (445), over NetBIOS (139), and over QUIC (443).

crates/smb-dtyp/src/binrw_util.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
//! This module contains utility types for the binrw crate.
22
3+
pub mod boolean;
34
pub mod debug;
45
pub mod file_time;
56
pub mod fixed_string;
67
pub mod helpers;
8+
pub mod multi_sz;
79
pub mod pos_marker;
8-
pub mod sized_wide_string;
10+
pub mod sized_string;
911

1012
pub mod prelude {
13+
pub use super::boolean::Boolean;
1114
#[cfg(debug_assertions)]
1215
pub use super::debug::LogLocation;
1316
pub use super::file_time::FileTime;
1417
pub use super::helpers::*;
18+
pub use super::multi_sz::MultiWSz;
1519
pub use super::pos_marker::PosMarker;
16-
pub use super::sized_wide_string::{
20+
pub use super::sized_string::{
1721
BaseSizedString, BaseSizedStringReadArgs, BaseSizedStringReadArgsBuilder, SizedAnsiString,
1822
SizedStringSize, SizedWideString,
1923
};
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! [`Boolean`][crate::Boolean] implementation for binrw.
2+
3+
use binrw::{Endian, prelude::*};
4+
use std::io::{Read, Seek, Write};
5+
6+
/// A simple Boolean type that reads and writes as a single byte.
7+
/// Any non-zero value is considered `true`, as defined by MS-FSCC 2.1.8.
8+
/// Similar to the WinAPI `BOOL` type.
9+
///
10+
/// This type supports `std::size_of::<Boolean>() == 1`, ensuring it is 1 byte in size.
11+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12+
pub struct Boolean(bool);
13+
14+
impl Boolean {
15+
const _VALIDATE_SIZE_OF: [u8; 1] = [0; size_of::<Self>()];
16+
}
17+
18+
impl BinRead for Boolean {
19+
type Args<'a> = ();
20+
21+
fn read_options<R: Read + Seek>(
22+
reader: &mut R,
23+
_: Endian,
24+
_: Self::Args<'_>,
25+
) -> binrw::BinResult<Self> {
26+
let value: u8 = u8::read_options(reader, Endian::Little, ())?;
27+
Ok(Boolean(value != 0))
28+
}
29+
}
30+
31+
impl BinWrite for Boolean {
32+
type Args<'a> = ();
33+
34+
fn write_options<W: Write + Seek>(
35+
&self,
36+
writer: &mut W,
37+
_: Endian,
38+
_: Self::Args<'_>,
39+
) -> binrw::BinResult<()> {
40+
let value: u8 = if self.0 { 1 } else { 0 };
41+
value.write_options(writer, Endian::Little, ())
42+
}
43+
}
44+
45+
impl From<bool> for Boolean {
46+
fn from(value: bool) -> Self {
47+
Boolean(value)
48+
}
49+
}
50+
51+
impl From<Boolean> for bool {
52+
fn from(val: Boolean) -> Self {
53+
val.0
54+
}
55+
}
56+
57+
#[cfg(test)]
58+
mod tests {
59+
use super::*;
60+
use smb_tests::*;
61+
62+
test_binrw! {
63+
Boolean => true: Boolean::from(true) => "01"
64+
}
65+
66+
test_binrw! {
67+
Boolean => false: Boolean::from(false) => "00"
68+
}
69+
70+
// Non-zero is considered true!
71+
test_binrw_read! {
72+
Boolean => true_non_zero: Boolean::from(true) => "17"
73+
}
74+
}

crates/smb-dtyp/src/binrw_util/fixed_string.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,14 @@ impl<const N: usize> std::fmt::Display for FixedAnsiString<N> {
129129

130130
impl<const N: usize> std::fmt::Display for FixedWideString<N> {
131131
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132-
super::sized_wide_string::display_utf16(self.as_slice(), f, core::iter::once)
132+
super::sized_string::display_utf16(self.as_slice(), f, core::iter::once)
133133
}
134134
}
135135

136136
#[cfg(test)]
137137
mod tests {
138138
use super::*;
139-
use smb_tests::{test_binrw, test_binrw_read_fail};
139+
use smb_tests::*;
140140

141141
type Ansi6 = FixedAnsiString<6>;
142142

Lines changed: 39 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use binrw::{Endian, NullWideString, prelude::*};
2-
use std::io::{Read, Seek, Write};
3-
use std::ops::{Deref, DerefMut};
1+
use binrw::{Endian, prelude::*};
2+
use std::io::SeekFrom;
43

54
#[binrw::writer(writer, endian)]
65
pub fn write_u48(value: &u64) -> binrw::BinResult<()> {
@@ -23,6 +22,26 @@ pub fn read_u48() -> binrw::BinResult<u64> {
2322
Ok(conv(buf))
2423
}
2524

25+
/// Utility binrw parser function that reads an optional value of type `T`
26+
/// if there is _ANY_ data left in the stream
27+
/// (any data, not enough data - for Option<u32> it's 1 byte in the stream).
28+
#[binrw::parser(reader, endian)]
29+
pub fn binread_if_has_data<T>() -> BinResult<Option<T>>
30+
where
31+
for<'a> T: BinRead<Args<'a> = ()>,
32+
{
33+
let current_pos = reader.stream_position()?;
34+
let stream_len = reader.seek(SeekFrom::End(0))?;
35+
reader.seek(SeekFrom::Start(current_pos))?;
36+
37+
let data_left = stream_len - current_pos;
38+
if data_left > 0 {
39+
Ok(Some(T::read_options(reader, endian, ())?))
40+
} else {
41+
Ok(None)
42+
}
43+
}
44+
2645
#[cfg(test)]
2746
mod test {
2847
use std::io::Cursor;
@@ -78,119 +97,25 @@ mod test {
7897
PARSED_BE.write_be(&mut Cursor::new(&mut buf)).unwrap();
7998
assert_eq!(buf, DATA_BYTES);
8099
}
81-
}
82-
83-
/// A simple Boolean type that reads and writes as a single byte.
84-
/// Any non-zero value is considered `true`, as defined by MS-FSCC 2.1.8.
85-
/// Similar to the WinAPI `BOOL` type.
86-
///
87-
/// This type supports `std::size_of::<Boolean>() == 1`, ensuring it is 1 byte in size.
88-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89-
pub struct Boolean(bool);
90-
91-
impl Boolean {
92-
const _VALIDATE_SIZE_OF: [u8; 1] = [0; size_of::<Self>()];
93-
}
94-
95-
impl BinRead for Boolean {
96-
type Args<'a> = ();
97-
98-
fn read_options<R: Read + Seek>(
99-
reader: &mut R,
100-
_: Endian,
101-
_: Self::Args<'_>,
102-
) -> binrw::BinResult<Self> {
103-
let value: u8 = u8::read_options(reader, Endian::Little, ())?;
104-
Ok(Boolean(value != 0))
105-
}
106-
}
107-
108-
impl BinWrite for Boolean {
109-
type Args<'a> = ();
110-
111-
fn write_options<W: Write + Seek>(
112-
&self,
113-
writer: &mut W,
114-
_: Endian,
115-
_: Self::Args<'_>,
116-
) -> binrw::BinResult<()> {
117-
let value: u8 = if self.0 { 1 } else { 0 };
118-
value.write_options(writer, Endian::Little, ())
119-
}
120-
}
121100

122-
impl From<bool> for Boolean {
123-
fn from(value: bool) -> Self {
124-
Boolean(value)
125-
}
126-
}
127-
128-
impl From<Boolean> for bool {
129-
fn from(val: Boolean) -> Self {
130-
val.0
131-
}
132-
}
133-
134-
/// A MultiSz (Multiple Null-terminated Wide Strings) type that reads and writes a sequence of
135-
/// null-terminated wide strings, ending with an additional null string.
136-
///
137-
/// Similar to the Registry [`REG_MULTI_SZ`](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types) type.
138-
#[derive(Debug, Clone, PartialEq, Eq)]
139-
pub struct MultiSz(Vec<NullWideString>);
140-
141-
impl BinRead for MultiSz {
142-
type Args<'a> = ();
143-
144-
fn read_options<R: Read + Seek>(
145-
reader: &mut R,
146-
endian: Endian,
147-
_: Self::Args<'_>,
148-
) -> BinResult<Self> {
149-
let mut strings = Vec::new();
150-
loop {
151-
let string: NullWideString = NullWideString::read_options(reader, endian, ())?;
152-
if string.is_empty() {
153-
break;
154-
}
155-
strings.push(string);
156-
}
157-
Ok(MultiSz(strings))
158-
}
159-
}
160-
161-
impl BinWrite for MultiSz {
162-
type Args<'a> = ();
163-
164-
fn write_options<W: Write + Seek>(
165-
&self,
166-
writer: &mut W,
167-
endian: Endian,
168-
_: Self::Args<'_>,
169-
) -> BinResult<()> {
170-
for string in &self.0 {
171-
string.write_options(writer, endian, ())?;
172-
}
173-
NullWideString::default().write_options(writer, endian, ())?;
174-
Ok(())
175-
}
176-
}
177-
178-
impl Deref for MultiSz {
179-
type Target = Vec<NullWideString>;
180-
181-
fn deref(&self) -> &Self::Target {
182-
&self.0
183-
}
184-
}
185-
186-
impl DerefMut for MultiSz {
187-
fn deref_mut(&mut self) -> &mut Self::Target {
188-
&mut self.0
101+
#[binrw::binrw]
102+
#[derive(Debug, PartialEq, Eq)]
103+
struct TestBinReadIfHasData {
104+
#[br(parse_with = super::binread_if_has_data)]
105+
pub val1: Option<u8>,
189106
}
190-
}
191107

192-
impl From<Vec<NullWideString>> for MultiSz {
193-
fn from(strings: Vec<NullWideString>) -> Self {
194-
MultiSz(strings)
108+
#[test]
109+
fn test_if_has_data() {
110+
// with data
111+
let data_with = [0x42u8];
112+
let mut reader = Cursor::new(&data_with);
113+
let parsed = TestBinReadIfHasData::read_le(&mut reader).unwrap();
114+
assert_eq!(parsed, TestBinReadIfHasData { val1: Some(0x42) });
115+
// without data
116+
let data_without: [u8; 0] = [];
117+
let mut reader = Cursor::new(&data_without);
118+
let parsed = TestBinReadIfHasData::read_le(&mut reader).unwrap();
119+
assert_eq!(parsed, TestBinReadIfHasData { val1: None });
195120
}
196121
}

0 commit comments

Comments
 (0)