Skip to main content

wireforge_app/smb2/
negotiate.rs

1//! SMB2 negotiate protocol request and response parsers.
2
3pub struct Smb2NegotiateRequest<'a> { buf: &'a [u8] }
4
5impl<'a> Smb2NegotiateRequest<'a> {
6    pub fn parse(buf: &'a [u8]) -> Option<Self> {
7        // header(36) = struct_size(2) + dialect_count(2) + sec_mode(2) + reserved(2)
8        //             + capabilities(4) + client_guid(16) + client_start_time(8)
9        if buf.len() < 36 { return None; }
10        if u16::from_le_bytes([buf[0], buf[1]]) != 36 { return None; }
11        Some(Self { buf })
12    }
13
14    pub fn dialect_count(&self) -> u16 {
15        u16::from_le_bytes([self.buf[2], self.buf[3]])
16    }
17
18    pub fn capabilities(&self) -> u32 {
19        u32::from_le_bytes([self.buf[8], self.buf[9], self.buf[10], self.buf[11]])
20    }
21
22    /// 16-byte client GUID at offset 12.
23    pub fn client_guid(&self) -> &[u8; 16] {
24        self.buf[12..28].try_into().unwrap()
25    }
26
27    /// Dialects start at offset 36.
28    pub fn dialects(&self) -> Vec<u16> {
29        let cnt = self.dialect_count() as usize;
30        (0..cnt)
31            .filter_map(|i| {
32                let off = 36 + i * 2;
33                if off + 2 <= self.buf.len() {
34                    Some(u16::from_le_bytes([self.buf[off], self.buf[off + 1]]))
35                } else {
36                    None
37                }
38            })
39            .collect()
40    }
41}
42
43pub struct Smb2NegotiateResponse<'a> { buf: &'a [u8] }
44
45impl<'a> Smb2NegotiateResponse<'a> {
46    pub fn parse(buf: &'a [u8]) -> Option<Self> {
47        // header(65) = struct_size(2) + sec_mode(2) + dialect_rev(2) + reserved(2)
48        //             + server_guid(16) + capabilities(4) + max_transact(4) + max_read(4)
49        //             + max_write(4) + system_time(8) + server_start_time(8)
50        //             + sec_buf_offset(2) + sec_buf_len(2) + reserved2(4) + padding(1)
51        if buf.len() < 65 { return None; }
52        if u16::from_le_bytes([buf[0], buf[1]]) != 65 { return None; }
53        Some(Self { buf })
54    }
55
56    pub fn dialect_revision(&self) -> u16 {
57        u16::from_le_bytes([self.buf[4], self.buf[5]])
58    }
59
60    pub fn server_guid(&self) -> &[u8; 16] {
61        self.buf[8..24].try_into().unwrap()
62    }
63
64    pub fn capabilities(&self) -> u32 {
65        u32::from_le_bytes([self.buf[24], self.buf[25], self.buf[26], self.buf[27]])
66    }
67
68    pub fn max_transact_size(&self) -> u32 {
69        u32::from_le_bytes([self.buf[28], self.buf[29], self.buf[30], self.buf[31]])
70    }
71
72    pub fn max_read_size(&self) -> u32 {
73        u32::from_le_bytes([self.buf[32], self.buf[33], self.buf[34], self.buf[35]])
74    }
75
76    pub fn max_write_size(&self) -> u32 {
77        u32::from_le_bytes([self.buf[36], self.buf[37], self.buf[38], self.buf[39]])
78    }
79
80    pub fn system_time(&self) -> u64 {
81        u64::from_le_bytes([
82            self.buf[40], self.buf[41], self.buf[42], self.buf[43],
83            self.buf[44], self.buf[45], self.buf[46], self.buf[47],
84        ])
85    }
86
87    pub fn server_start_time(&self) -> u64 {
88        u64::from_le_bytes([
89            self.buf[48], self.buf[49], self.buf[50], self.buf[51],
90            self.buf[52], self.buf[53], self.buf[54], self.buf[55],
91        ])
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn parse_negotiate_request() {
101        let mut body = vec![36u8, 0, 4, 0, 0, 0, 0, 0]; // sz+count+sec_mode+reserved
102        body.extend_from_slice(&1u32.to_le_bytes()); // capabilities (offset 8)
103        body.extend_from_slice(&[0xAAu8; 16]); // client_guid (offset 12)
104        body.extend_from_slice(&0u64.to_le_bytes()); // client_start_time (offset 28)
105        body.extend_from_slice(&0x0202u16.to_le_bytes()); // dialects (offset 36)
106        body.extend_from_slice(&0x0210u16.to_le_bytes());
107        body.extend_from_slice(&0x0300u16.to_le_bytes());
108        body.extend_from_slice(&0x0311u16.to_le_bytes());
109        let req = Smb2NegotiateRequest::parse(&body).unwrap();
110        assert_eq!(req.dialect_count(), 4);
111        assert_eq!(req.dialects(), vec![0x0202, 0x0210, 0x0300, 0x0311]);
112    }
113
114    #[test]
115    fn parse_negotiate_response() {
116        let mut body = vec![65u8, 0, 0, 0, 0x11, 0x03, 0, 0]; // sz+sec+rev+reserved
117        body.extend_from_slice(&[0u8; 16]); // server_guid (offset 8)
118        body.extend_from_slice(&0u32.to_le_bytes()); // capabilities (offset 24)
119        body.extend_from_slice(&65536u32.to_le_bytes()); // max_transact (offset 28)
120        body.extend_from_slice(&65536u32.to_le_bytes()); // max_read (offset 32)
121        body.extend_from_slice(&65536u32.to_le_bytes()); // max_write (offset 36)
122        body.extend_from_slice(&0u64.to_le_bytes()); // system_time (offset 40)
123        body.extend_from_slice(&0u64.to_le_bytes()); // server_start_time (offset 48)
124        body.extend_from_slice(&[0u8; 9]); // sec_buf_off+len+reserved2 + 1 byte padding
125        let resp = Smb2NegotiateResponse::parse(&body).unwrap();
126        assert_eq!(resp.dialect_revision(), 0x0311);
127        assert_eq!(resp.max_read_size(), 65536);
128    }
129}