Skip to main content

smb_server_proto_smb2/
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/// First byte of a plaintext SMB2 message ProtocolId (`SMB2_MAGIC[0]`, `\xFE`).
20pub const PROTO_ID_SMB2: u8 = SMB2_MAGIC[0];
21/// First byte of an encrypted TRANSFORM_HEADER ProtocolId (`\xFD`, [MS-SMB2] §2.2.41).
22pub const PROTO_ID_ENCRYPTED: u8 = commands::TF_MAGIC[0];
23/// First byte of a COMPRESSION_TRANSFORM_HEADER ProtocolId (`\xFC`, [MS-SMB2] §2.2.42).
24pub const PROTO_ID_COMPRESSED: u8 = compress::PROTOCOL_ID[0];
25
26pub mod commands;
27pub mod compress;
28pub mod consts;
29pub mod info;
30pub mod negotiate;
31pub mod session_setup;
32
33/// Known SMB2/3 dialect numbers (§2.2.3.1.1).
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Dialect {
36    /// 2.0.2
37    V202,
38    /// 2.1.0
39    V210,
40    /// 3.0
41    V300,
42    /// 3.0.2
43    V302,
44    /// 3.1.1
45    V311,
46}
47
48impl Dialect {
49    /// Wire dialect revision number.
50    pub fn rev(self) -> u16 {
51        match self {
52            Dialect::V202 => 0x0202,
53            Dialect::V210 => 0x0210,
54            Dialect::V300 => 0x0300,
55            Dialect::V302 => 0x0302,
56            Dialect::V311 => 0x0311,
57        }
58    }
59}
60
61/// Fixed 64-byte SMB2 header (§2.2.1) — decoded view.
62#[derive(Debug, Clone)]
63pub struct Header2 {
64    /// Credit charge (SMB3.1.1) / structure size.
65    pub credit_charge: u16,
66    /// Status.
67    pub status: u32,
68    /// Command code (§2.2.1.2).
69    pub command: u16,
70    /// Credits granted by server.
71    pub credits: u16,
72    /// Header flags.
73    pub flags: u32,
74    /// Next command chain offset (compounding).
75    pub next_command: u32,
76    /// Message identifier.
77    pub message_id: u64,
78    /// Tree identifier (0 when not tree-bound).
79    pub tree_id: u32,
80    /// Session identifier.
81    pub session_id: u64,
82    /// Signature / dedupe tag.
83    pub signature: [u8; 16],
84}
85
86impl Header2 {
87    /// Parse the 64-byte header from `buf`; `None` on bad magic/size.
88    pub fn parse(buf: &[u8]) -> Option<Header2> {
89        if buf.len() < 64 || buf[0..4] != SMB2_MAGIC {
90            return None;
91        }
92        Some(Header2 {
93            credit_charge: u16::from_le_bytes(buf[6..8].try_into().ok()?),
94            status: u32::from_le_bytes(buf[8..12].try_into().ok()?),
95            command: u16::from_le_bytes(buf[12..14].try_into().ok()?),
96            credits: u16::from_le_bytes(buf[14..16].try_into().ok()?),
97            flags: u32::from_le_bytes(buf[16..20].try_into().ok()?),
98            next_command: u32::from_le_bytes(buf[20..24].try_into().ok()?),
99            message_id: u64::from_le_bytes(buf[24..32].try_into().ok()?),
100            // ProcessId/Reserved occupies 32..36; TreeId 36..40; SessionId
101            // spans 40..48 ([MS-SMB2] §2.2.1).
102            tree_id: u32::from_le_bytes(buf[36..40].try_into().ok()?),
103            session_id: u64::from_le_bytes(buf[40..48].try_into().ok()?),
104            signature: buf[48..64].try_into().ok()?,
105        })
106    }
107
108    /// True when the response flag (bit 0) is set.
109    pub fn is_response(&self) -> bool {
110        self.flags & 0x0000_0001 != 0
111    }
112
113    /// True when the signed flag (bit 2) is set.
114    pub fn is_signed(&self) -> bool {
115        self.flags & 0x0000_0008 != 0
116    }
117
118    /// True when the async flag is set: `Flags & SMB2_FLAGS_ASYNC_COMMAND`
119    /// (0x00000002, [MS-SMB2] §2.2.1.2).
120    pub fn is_async(&self) -> bool {
121        self.flags & 0x0000_0002 != 0
122    }
123}