smb_server_proto_smb3/lib.rs
1//! SMB2 wire structures ([MS-SMB2]).
2//!
3//! Pass-2 scaffold. Planned modules against the spec stored at
4//! `docs/protocol/smb2_3/MS-SMB2.pdf`:
5//! - header: 64-byte SMB2 header (§2.2.1)
6//! - negotiate: §2.2.3 (dialects 2.0.2/2.1.0/3.x, capabilities, contexts)
7//! - session: SESSION_SETUP §2.2.5, LOGOFF §2.2.7
8//! - tree: TREE_CONNECT/CONNECTX/DISCONNECT §2.2.9–2.2.11
9//! - create/read/write/close/flush: §2.2.13–2.2.17
10//! - find/query/set info/dir: §2.2.33–2.2.37
11//! - credits: credit grant/charge accounting (§3.3.1.1)
12#![forbid(unsafe_code)]
13#![deny(missing_docs)]
14#![warn(missing_debug_implementations)]
15
16/// SMB2 header magic `\xFESMB`.
17pub const SMB2_MAGIC: [u8; 4] = [0xFE, b'S', b'M', b'B'];
18
19/// Known SMB2/3 dialect numbers (§2.2.3.1.1).
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Dialect {
22 /// 2.0.2
23 V202,
24 /// 2.1.0
25 V210,
26 /// 3.0
27 V300,
28 /// 3.0.2
29 V302,
30 /// 3.1.1
31 V311,
32}
33
34/// Fixed 64-byte SMB2 header (§2.2.1) — decoded view.
35#[derive(Debug, Clone)]
36pub struct Header2 {
37 /// Credit charge (SMB3.1.1) / structure size.
38 pub credit_charge: u16,
39 /// Status.
40 pub status: u32,
41 /// Command code (§2.2.1.2).
42 pub command: u16,
43 /// Credits granted by server.
44 pub credits: u16,
45 /// Header flags.
46 pub flags: u32,
47 /// Next command chain offset (compounding).
48 pub next_command: u32,
49 /// Message identifier.
50 pub message_id: u64,
51 /// Session identifier.
52 pub session_id: u64,
53 /// Signature / dedupe tag.
54 pub signature: [u8; 16],
55}
56
57impl Header2 {
58 /// Parse the 64-byte header from `buf`; `None` on bad magic/size.
59 pub fn parse(buf: &[u8]) -> Option<Header2> {
60 if buf.len() < 64 || buf[0..4] != SMB2_MAGIC {
61 return None;
62 }
63 Some(Header2 {
64 credit_charge: u16::from_le_bytes(buf[6..8].try_into().ok()?),
65 status: u32::from_le_bytes(buf[8..12].try_into().ok()?),
66 command: u16::from_le_bytes(buf[12..14].try_into().ok()?),
67 credits: u16::from_le_bytes(buf[14..16].try_into().ok()?),
68 flags: u32::from_le_bytes(buf[16..20].try_into().ok()?),
69 next_command: u32::from_le_bytes(buf[20..24].try_into().ok()?),
70 message_id: u64::from_le_bytes(buf[24..32].try_into().ok()?),
71 session_id: u64::from_le_bytes(buf[36..44].try_into().ok()?),
72 signature: buf[48..64].try_into().ok()?,
73 })
74 }
75
76 /// True when the response flag (bit 0) is set.
77 pub fn is_response(&self) -> bool {
78 self.flags & 0x0000_0001 != 0
79 }
80}