use crate::msg::header::Header;
use crate::msg::transform::{CompressionTransformHeader, TransformHeader};
use crate::pack::{ReadCursor, Unpack};
use crate::types::Command;
pub fn fuzz_header_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = Header::unpack(&mut cursor);
}
pub fn fuzz_transform_header_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = TransformHeader::unpack(&mut cursor);
}
pub fn fuzz_compression_transform_header_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = CompressionTransformHeader::unpack(&mut cursor);
}
pub fn fuzz_compound_split(data: &[u8]) {
let _ = crate::client::connection::split_compound(data);
}
pub fn fuzz_frame_parse(data: &[u8]) {
let subs = match crate::client::connection::split_compound(data) {
Ok(s) => s,
Err(_) => return,
};
for sub in subs {
let mut cursor = ReadCursor::new(&sub);
let _ = Header::unpack(&mut cursor);
}
}
pub fn fuzz_sub_frame_parse(data: &[u8]) {
if data.len() < Header::SIZE {
return;
}
let mut cursor = ReadCursor::new(data);
let header = match Header::unpack(&mut cursor) {
Ok(h) => h,
Err(_) => return,
};
let body = &data[Header::SIZE..];
let is_response = header.is_response();
dispatch_body(header.command, is_response, body);
}
fn dispatch_body(command: Command, is_response: bool, body: &[u8]) {
use crate::msg;
macro_rules! try_unpack {
($ty:ty) => {{
let mut cursor = ReadCursor::new(body);
let _ = <$ty as Unpack>::unpack(&mut cursor);
}};
}
match (command, is_response) {
(Command::Negotiate, false) => try_unpack!(msg::negotiate::NegotiateRequest),
(Command::Negotiate, true) => try_unpack!(msg::negotiate::NegotiateResponse),
(Command::SessionSetup, false) => try_unpack!(msg::session_setup::SessionSetupRequest),
(Command::SessionSetup, true) => try_unpack!(msg::session_setup::SessionSetupResponse),
(Command::Logoff, false) => try_unpack!(msg::logoff::LogoffRequest),
(Command::Logoff, true) => try_unpack!(msg::logoff::LogoffResponse),
(Command::TreeConnect, false) => try_unpack!(msg::tree_connect::TreeConnectRequest),
(Command::TreeConnect, true) => try_unpack!(msg::tree_connect::TreeConnectResponse),
(Command::TreeDisconnect, false) => {
try_unpack!(msg::tree_disconnect::TreeDisconnectRequest)
}
(Command::TreeDisconnect, true) => {
try_unpack!(msg::tree_disconnect::TreeDisconnectResponse)
}
(Command::Create, false) => try_unpack!(msg::create::CreateRequest),
(Command::Create, true) => try_unpack!(msg::create::CreateResponse),
(Command::Close, false) => try_unpack!(msg::close::CloseRequest),
(Command::Close, true) => try_unpack!(msg::close::CloseResponse),
(Command::Flush, false) => try_unpack!(msg::flush::FlushRequest),
(Command::Flush, true) => try_unpack!(msg::flush::FlushResponse),
(Command::Read, false) => try_unpack!(msg::read::ReadRequest),
(Command::Read, true) => try_unpack!(msg::read::ReadResponse),
(Command::Write, false) => try_unpack!(msg::write::WriteRequest),
(Command::Write, true) => try_unpack!(msg::write::WriteResponse),
(Command::Lock, false) => try_unpack!(msg::lock::LockRequest),
(Command::Lock, true) => try_unpack!(msg::lock::LockResponse),
(Command::Ioctl, false) => try_unpack!(msg::ioctl::IoctlRequest),
(Command::Ioctl, true) => try_unpack!(msg::ioctl::IoctlResponse),
(Command::Cancel, false) => try_unpack!(msg::cancel::CancelRequest),
(Command::Echo, false) => try_unpack!(msg::echo::EchoRequest),
(Command::Echo, true) => try_unpack!(msg::echo::EchoResponse),
(Command::QueryDirectory, false) => {
try_unpack!(msg::query_directory::QueryDirectoryRequest)
}
(Command::QueryDirectory, true) => {
try_unpack!(msg::query_directory::QueryDirectoryResponse)
}
(Command::ChangeNotify, false) => try_unpack!(msg::change_notify::ChangeNotifyRequest),
(Command::ChangeNotify, true) => try_unpack!(msg::change_notify::ChangeNotifyResponse),
(Command::QueryInfo, false) => try_unpack!(msg::query_info::QueryInfoRequest),
(Command::QueryInfo, true) => try_unpack!(msg::query_info::QueryInfoResponse),
(Command::SetInfo, false) => try_unpack!(msg::set_info::SetInfoRequest),
(Command::SetInfo, true) => try_unpack!(msg::set_info::SetInfoResponse),
_ => {}
}
}
pub fn fuzz_negotiate_request_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = crate::msg::negotiate::NegotiateRequest::unpack(&mut cursor);
}
pub fn fuzz_negotiate_response_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = crate::msg::negotiate::NegotiateResponse::unpack(&mut cursor);
}
pub fn fuzz_create_request_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = crate::msg::create::CreateRequest::unpack(&mut cursor);
}
pub fn fuzz_create_response_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = crate::msg::create::CreateResponse::unpack(&mut cursor);
}
pub fn fuzz_query_info_response_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = crate::msg::query_info::QueryInfoResponse::unpack(&mut cursor);
}
pub fn fuzz_dfs_referral_response_parse(data: &[u8]) {
let mut cursor = ReadCursor::new(data);
let _ = crate::msg::dfs::RespGetDfsReferral::unpack(&mut cursor);
}
pub fn fuzz_name_round_trip(data: &[u8]) {
let Ok(name) = std::str::from_utf8(data) else {
return;
};
let encoded_name = crate::name::encode_name(name);
assert_wire_safe(&encoded_name, name);
let encoded_path = crate::name::encode_path(name);
for component in encoded_path.split('\\') {
if component != "." && component != ".." {
assert_wire_safe(component, name);
}
}
if name
.chars()
.any(|c| (0xF001..=0xF029).contains(&(c as u32)))
{
return;
}
assert_eq!(
crate::name::decode_name(&encoded_name),
name,
"decode_name did not undo encode_name for {name:?}"
);
if !name.starts_with('/') {
assert_eq!(
crate::name::decode_path(&encoded_path),
name,
"decode_path did not undo encode_path for {name:?}"
);
}
}
fn assert_wire_safe(encoded: &str, original: &str) {
assert!(
!encoded.contains(['"', '*', ':', '<', '>', '?', '\\', '|']),
"encoding {original:?} left a character SMB2 rejects"
);
assert!(
!encoded.chars().any(|c| ('\u{01}'..='\u{1F}').contains(&c)),
"encoding {original:?} left a control character"
);
assert!(
!matches!(encoded.chars().next_back(), Some(' ') | Some('.')),
"encoding {original:?} left a trailing space or period"
);
}