Skip to main content

smb_server_proto_smb1/
query.rs

1//! QUERY_INFORMATION payload encoders for the information levels
2//! ([MS-SMB] §2.2.8.2–4).
3
4use smb_server_proto::buf::Writer;
5use smb_server_proto::types::AttrFlags;
6
7/// Neutral metadata snapshot used by query payload encoders.
8#[derive(Debug, Clone, Default)]
9pub struct QueryMeta {
10    /// Creation / access / write / change FILETIMEs.
11    pub times: [u64; 4],
12    /// Attribute flags.
13    pub attrs: AttrFlags,
14    /// End-of-file size.
15    pub eof: u64,
16    /// Allocation size.
17    pub alloc: u64,
18    /// Directory flag.
19    pub is_dir: bool,
20}
21
22/// Encode a QUERY_PATH/FILE information payload for `level` from neutral
23/// metadata. Returns `None` for levels this server does not implement —
24/// callers translate that to `INVALID_PARAMETER`.
25///
26/// `name` is required by the NAME/ALT_NAME levels.
27pub fn encode_payload(
28    level: u16,
29    m: &QueryMeta,
30    name: &str,
31) -> Option<Vec<u8>> {
32    let times = &m.times;
33    let eof = if m.is_dir { 0 } else { m.eof };
34    let alloc = eof.div_ceil(4096) * 4096;
35
36    let mut d = Writer::new(0);
37    match level {
38        0x004 | 0x1004 | 0x101 => {
39            // BASIC: creation, access, write, change + attributes.
40            for t in &times[..3] {
41                d.push_u64(*t);
42            }
43            d.push_u64(times[3]);
44            d.raw(&m.attrs.0.to_le_bytes());
45        }
46        0x005 => {
47            // STANDARD (22-byte non-passthrough form).
48            d.push_u64(alloc);
49            d.push_u64(eof);
50            d.push_u32(1); // links
51            d.push(if m.attrs.0 & 0x1 != 0 { 1 } else { 0 }); // delete pending
52            d.push(m.is_dir as u8);
53        }
54        0x1005 => {
55            // STANDARD pass-through (24-byte form with reserved word).
56            d.push_u64(alloc);
57            d.push_u64(eof);
58            d.push_u32(1); // links
59            d.push(0); // delete pending
60            d.push(m.is_dir as u8);
61            d.push_u16(0); // reserved
62        }
63        0x006 | 0x1007 => {
64            d.push_u32(0); // EA size
65        }
66        0x007 => {
67            d.push_u32(4); // all-EA list length
68            d.push_u32(0); // full list length
69        }
70        0x1006 => {
71            d.push_u64(0); // index number placeholder
72        }
73        0x1008 => {
74            d.push_u32(0x0012_0089 | 0x0000_0016); // read+write access flags
75        }
76        0x1009 | 0x1015 => {
77            let units: Vec<u16> = name.encode_utf16().collect();
78            d.push_u32((units.len() * 2) as u32);
79            for u in units {
80                d.push_u16(u);
81            }
82        }
83        0x100B | 0x103 => {
84            d.push_u64(alloc);
85        }
86        0x100C | 0x104 => {
87            d.push_u64(eof);
88        }
89        0x100D => {
90            d.push(0);
91        }
92        0x1012 => {
93            for t in times.iter() {
94                d.push_u64(*t);
95            }
96            d.push_u64(alloc);
97            d.push_u64(eof);
98            d.push_u32(1); // links
99            d.push(0); // delete pending
100            d.push(m.is_dir as u8);
101            d.push_u16(0); // reserved
102            d.push_u32(0); // EA size
103            d.push_u32(0x0012_0089 | 0x16); // access flags
104            d.push_u64(0); // current byte offset
105            d.raw(&m.attrs.0.to_le_bytes());
106        }
107        0x010 | 0x1016 => {
108            d.push_u32(0); // empty stream list
109        }
110        0x107 => {
111            // Composite getattr: times, attrs, rsvd, alloc, eof, links,
112            // del/dir flags, pad, index, namelen, name.
113            for t in times.iter() {
114                d.push_u64(*t);
115            }
116            d.raw(&m.attrs.0.to_le_bytes());
117            d.push_u32(0); // reserved
118            d.push_u64(alloc);
119            d.push_u64(eof);
120            d.push_u32(1); // links
121            d.push(0); // delete pending
122            d.push(m.is_dir as u8);
123            d.push_u16(0); // pad
124            d.push_u32(0); // index
125            let units: Vec<u16> = name.encode_utf16().collect();
126            d.push_u32((units.len() * 2) as u32);
127            for u in units {
128                d.push_u16(u);
129            }
130        }
131        _ => return None,
132    }
133    Some(d.into_inner())
134}