Skip to main content

smb_server_proto_smb1/
misc.rs

1//! Small commands: CLOSE, FLUSH, SEEK, ECHO, LOGOFF_ANDX, TREE_DISCONNECT,
2//! QUERY_INFORMATION_DISK, PROCESS_EXIT and LOCKING_ANDX.
3
4
5fn u16le(w: &[u8], off: usize) -> u16 {
6    w.get(off..off + 2)
7        .map(|s| u16::from_le_bytes(s.try_into().unwrap()))
8        .unwrap_or(0)
9}
10
11/// CLOSE request (WC=3): FID plus optional mtime.
12#[derive(Debug)]
13pub struct CloseReq {
14    /// Handle to close.
15    pub fid: u16,
16    /// Client-supplied last-write time (0 = leave unchanged).
17    pub mtime: u32,
18}
19
20impl CloseReq {
21    /// Parse WC=3 words.
22    pub fn parse(words: &[u8]) -> Option<Self> {
23        if words.len() < 6 {
24            return None;
25        }
26        Some(CloseReq {
27            fid: u16le(words, 0),
28            mtime: u32le(words, 2),
29        })
30    }
31}
32
33/// Empty WC=0 response body.
34pub fn empty_response(_command: u8) -> (Vec<u8>, Vec<u8>) {
35    (Vec::new(), Vec::new())
36}
37
38/// FLUSH request: FID or `0xFFFF` for all handles of the PID.
39pub fn flush_fid(words: &[u8]) -> Option<u16> {
40    words.len().checked_sub(2).map(|_| u16le(words, 0))
41}
42
43/// ECHO request: repeat count plus echo data.
44#[derive(Debug)]
45pub struct EchoReq {
46    /// Number of responses requested.
47    pub count: u16,
48    /// Data to be echoed back verbatim.
49    pub data: Vec<u8>,
50}
51
52impl EchoReq {
53    /// Parse WC=1 request; remaining bytes are the echoed payload.
54    pub fn parse(words: &[u8], data: &[u8]) -> Self {
55        EchoReq { count: u16le(words, 0).max(1), data: data.to_vec() }
56    }
57
58    /// Build one echo response with sequence number `seq`.
59    pub fn build_response(seq: u16, data: &[u8]) -> (Vec<u8>, Vec<u8>) {
60        let mut params = Vec::with_capacity(4);
61        params.extend_from_slice(&seq.to_le_bytes());
62        params.extend_from_slice(&0u16.to_le_bytes());
63        let mut bytes = Vec::new();
64        bytes.extend_from_slice(data);
65        (params, bytes)
66    }
67}
68
69/// LOGOFF_ANDX response (AndX triple only).
70#[derive(Debug)]
71pub struct LogoffResp;
72impl LogoffResp {
73    /// Build WC=2 terminal AndX body.
74    pub fn body() -> (Vec<u8>, Vec<u8>) {
75        (vec![0xFF, 0, 0, 0], Vec::new())
76    }
77}
78
79/// TREE_DISCONNECT response (WC=0).
80#[derive(Debug)]
81pub struct TreeDisconnectResp;
82impl TreeDisconnectResp {
83    /// Build WC=0 body.
84    pub fn body() -> (Vec<u8>, Vec<u8>) {
85        (Vec::new(), Vec::new())
86    }
87}
88
89/// QUERY_INFORMATION_DISK response (WC=5).
90#[derive(Debug)]
91pub struct QueryDiskInfo {
92    /// Total allocation units.
93    pub total_units: u32,
94    /// Free allocation units.
95    pub free_units: u32,
96    /// Bytes per sector.
97    pub bps: u16,
98    /// Sectors per cluster/unit.
99    pub spc: u16,
100}
101
102impl QueryDiskInfo {
103    /// Encode WC=5 parameter block.
104    pub fn encode(&self) -> Vec<u8> {
105        let mut p = Vec::with_capacity(10);
106        p.extend_from_slice(&self.total_units.to_le_bytes());
107        p.extend_from_slice(&self.free_units.to_le_bytes());
108        p.extend_from_slice(&self.bps.to_le_bytes());
109        p.extend_from_slice(&self.spc.to_le_bytes());
110        p.extend_from_slice(&0u16.to_le_bytes()); // reserved
111        p
112    }
113}
114
115/// SEEK request ([MS-CIFS] §2.2.4.x): FID, mode, offset.
116#[derive(Debug)]
117pub struct SeekReq {
118    /// Open file identifier.
119    pub fid: u16,
120    /// Origin mode (0 start / 1 current / 2 end).
121    pub mode: u16,
122    /// Signed offset from that origin.
123    pub offset: i64,
124}
125
126impl SeekReq {
127    /// Parse WC=4 words.
128    pub fn parse(words: &[u8]) -> Option<Self> {
129        if words.len() < 8 {
130            return None;
131        }
132        Some(SeekReq {
133            fid: u16le(words, 0),
134            mode: u16le(words, 2),
135            offset: i32::from_le_bytes(words.get(4..8)?.try_into().ok()?) as i64,
136        })
137    }
138
139    /// Build WC=2 response carrying the resulting position.
140    pub fn build_response(new_pos: u64) -> Vec<u8> {
141        (new_pos as u32).to_le_bytes().to_vec()
142    }
143}
144
145/// LOCKING_ANDX request — accepted but not enforced ([MS-CIFS] §2.2.4.32).
146#[derive(Debug)]
147pub struct LockingAndxReq;
148
149impl LockingAndxReq {
150    /// Build the WC=2 success response (terminal AndX triple).
151    pub fn build_response() -> Vec<u8> {
152        vec![0xFF, 0, 0, 0]
153    }
154}
155
156/// NT_CANCEL has no dedicated response beyond a CANCELLED status echo.
157#[derive(Debug)]
158pub struct NtCancel;
159
160fn u32le(w: &[u8], off: usize) -> u32 {
161    w.get(off..off + 4)
162        .map(|s| u32::from_le_bytes(s.try_into().unwrap()))
163        .unwrap_or(0)
164}