use crate::error::Result;
use crate::pack::{Pack, ReadCursor, Unpack, WriteCursor};
use crate::types::FileId;
use crate::Error;
pub const SMB2_READFLAG_READ_UNBUFFERED: u8 = 0x01;
pub const SMB2_READFLAG_REQUEST_COMPRESSED: u8 = 0x02;
pub const SMB2_CHANNEL_NONE: u32 = 0x0000_0000;
#[derive(Debug, Clone)]
pub struct ReadRequest {
pub padding: u8,
pub flags: u8,
pub length: u32,
pub offset: u64,
pub file_id: FileId,
pub minimum_count: u32,
pub channel: u32,
pub remaining_bytes: u32,
pub read_channel_info: Vec<u8>,
}
impl ReadRequest {
pub const STRUCTURE_SIZE: u16 = 49;
}
impl Pack for ReadRequest {
fn pack(&self, cursor: &mut WriteCursor) {
cursor.write_u16_le(Self::STRUCTURE_SIZE);
cursor.write_u8(self.padding);
cursor.write_u8(self.flags);
cursor.write_u32_le(self.length);
cursor.write_u64_le(self.offset);
cursor.write_u64_le(self.file_id.persistent);
cursor.write_u64_le(self.file_id.volatile);
cursor.write_u32_le(self.minimum_count);
cursor.write_u32_le(self.channel);
cursor.write_u32_le(self.remaining_bytes);
if self.read_channel_info.is_empty() {
cursor.write_u16_le(0);
cursor.write_u16_le(0);
} else {
cursor.write_u16_le(0); cursor.write_u16_le(self.read_channel_info.len() as u16);
}
if self.read_channel_info.is_empty() {
cursor.write_u8(0);
} else {
cursor.write_bytes(&self.read_channel_info);
}
}
}
impl Unpack for ReadRequest {
fn unpack(cursor: &mut ReadCursor<'_>) -> Result<Self> {
let structure_size = cursor.read_u16_le()?;
if structure_size != Self::STRUCTURE_SIZE {
return Err(Error::invalid_data(format!(
"invalid ReadRequest structure size: expected {}, got {}",
Self::STRUCTURE_SIZE,
structure_size
)));
}
let padding = cursor.read_u8()?;
let flags = cursor.read_u8()?;
let length = cursor.read_u32_le()?;
let offset = cursor.read_u64_le()?;
let persistent = cursor.read_u64_le()?;
let volatile = cursor.read_u64_le()?;
let minimum_count = cursor.read_u32_le()?;
let channel = cursor.read_u32_le()?;
let remaining_bytes = cursor.read_u32_le()?;
let _read_channel_info_offset = cursor.read_u16_le()?;
let read_channel_info_length = cursor.read_u16_le()?;
let read_channel_info = if read_channel_info_length > 0 {
cursor
.read_bytes(read_channel_info_length as usize)?
.to_vec()
} else {
cursor.skip(1)?;
Vec::new()
};
Ok(ReadRequest {
padding,
flags,
length,
offset,
file_id: FileId {
persistent,
volatile,
},
minimum_count,
channel,
remaining_bytes,
read_channel_info,
})
}
}
#[derive(Debug, Clone)]
pub struct ReadResponse {
pub data_offset: u8,
pub data_remaining: u32,
pub flags: u32,
pub data: Vec<u8>,
}
impl ReadResponse {
pub const STRUCTURE_SIZE: u16 = 17;
}
impl Pack for ReadResponse {
fn pack(&self, cursor: &mut WriteCursor) {
cursor.write_u16_le(Self::STRUCTURE_SIZE);
cursor.write_u8(self.data_offset);
cursor.write_u8(0); cursor.write_u32_le(self.data.len() as u32);
cursor.write_u32_le(self.data_remaining);
cursor.write_u32_le(self.flags); cursor.write_bytes(&self.data);
}
}
impl Unpack for ReadResponse {
fn unpack(cursor: &mut ReadCursor<'_>) -> Result<Self> {
let structure_size = cursor.read_u16_le()?;
if structure_size != Self::STRUCTURE_SIZE {
return Err(Error::invalid_data(format!(
"invalid ReadResponse structure size: expected {}, got {}",
Self::STRUCTURE_SIZE,
structure_size
)));
}
let data_offset = cursor.read_u8()?;
let _reserved = cursor.read_u8()?;
let data_length = cursor.read_u32_le()?;
let data_remaining = cursor.read_u32_le()?;
let flags = cursor.read_u32_le()?;
let data = if data_length > 0 {
cursor.read_bytes_bounded(data_length as usize)?.to_vec()
} else {
Vec::new()
};
Ok(ReadResponse {
data_offset,
data_remaining,
flags,
data,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_request_roundtrip() {
let original = ReadRequest {
padding: 0x50,
flags: SMB2_READFLAG_READ_UNBUFFERED,
length: 65536,
offset: 0x1000,
file_id: FileId {
persistent: 0xAAAA_BBBB_CCCC_DDDD,
volatile: 0x1111_2222_3333_4444,
},
minimum_count: 1024,
channel: SMB2_CHANNEL_NONE,
remaining_bytes: 0,
read_channel_info: Vec::new(),
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 49);
let mut r = ReadCursor::new(&bytes);
let decoded = ReadRequest::unpack(&mut r).unwrap();
assert_eq!(decoded.padding, original.padding);
assert_eq!(decoded.flags, original.flags);
assert_eq!(decoded.length, original.length);
assert_eq!(decoded.offset, original.offset);
assert_eq!(decoded.file_id, original.file_id);
assert_eq!(decoded.minimum_count, original.minimum_count);
assert_eq!(decoded.channel, original.channel);
assert_eq!(decoded.remaining_bytes, original.remaining_bytes);
assert!(decoded.read_channel_info.is_empty());
}
#[test]
fn read_request_with_channel_info_roundtrip() {
let channel_data = vec![0xDE, 0xAD, 0xBE, 0xEF];
let original = ReadRequest {
padding: 0,
flags: 0,
length: 4096,
offset: 0,
file_id: FileId {
persistent: 1,
volatile: 2,
},
minimum_count: 0,
channel: 0x0000_0001, remaining_bytes: 4096,
read_channel_info: channel_data.clone(),
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 52);
let mut r = ReadCursor::new(&bytes);
let decoded = ReadRequest::unpack(&mut r).unwrap();
assert_eq!(decoded.read_channel_info, channel_data);
assert_eq!(decoded.channel, 0x0000_0001);
}
#[test]
fn read_request_wrong_structure_size() {
let mut buf = [0u8; 49];
buf[0..2].copy_from_slice(&50u16.to_le_bytes());
let mut cursor = ReadCursor::new(&buf);
let result = ReadRequest::unpack(&mut cursor);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("structure size"), "error was: {err}");
}
#[test]
fn read_response_roundtrip() {
let original = ReadResponse {
data_offset: 0x50, data_remaining: 0,
flags: 0,
data: vec![0x01, 0x02, 0x03, 0x04, 0x05],
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 21);
let mut r = ReadCursor::new(&bytes);
let decoded = ReadResponse::unpack(&mut r).unwrap();
assert_eq!(decoded.data_offset, original.data_offset);
assert_eq!(decoded.data_remaining, original.data_remaining);
assert_eq!(decoded.flags, original.flags);
assert_eq!(decoded.data, original.data);
}
#[test]
fn read_response_empty_data() {
let original = ReadResponse {
data_offset: 0,
data_remaining: 0,
flags: 0,
data: Vec::new(),
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 16);
let mut r = ReadCursor::new(&bytes);
let decoded = ReadResponse::unpack(&mut r).unwrap();
assert!(decoded.data.is_empty());
}
#[test]
fn read_response_known_bytes() {
let mut buf = Vec::new();
buf.extend_from_slice(&17u16.to_le_bytes());
buf.push(0x50);
buf.push(0x00);
buf.extend_from_slice(&3u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&[0xAA, 0xBB, 0xCC]);
let mut cursor = ReadCursor::new(&buf);
let resp = ReadResponse::unpack(&mut cursor).unwrap();
assert_eq!(resp.data_offset, 0x50);
assert_eq!(resp.data, vec![0xAA, 0xBB, 0xCC]);
assert_eq!(resp.data_remaining, 0);
assert_eq!(resp.flags, 0);
}
#[test]
fn read_response_wrong_structure_size() {
let mut buf = [0u8; 16];
buf[0..2].copy_from_slice(&99u16.to_le_bytes());
let mut cursor = ReadCursor::new(&buf);
let result = ReadResponse::unpack(&mut cursor);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("structure size"), "error was: {err}");
}
}