Skip to main content

smb_server_proto/
buf.rs

1//! Cursor-style buffer reader/writer implementing SMB string and alignment
2//! rules ([MS-CIFS] §2.2.1.1.1 string formats, [MS-SMB] §2.2.2 extensions).
3//!
4//! All offsets handed to these helpers are *absolute frame offsets* — the
5//! caller adds the buffer's base once; the cursor tracks its own position
6//! internally.
7
8/// Reading cursor over a received buffer.
9#[derive(Debug)]
10pub struct Reader<'a> {
11    buf: &'a [u8],
12    pos: usize,
13}
14
15impl<'a> Reader<'a> {
16    /// New reader positioned at `pos` within `buf`.
17    pub fn new(buf: &'a [u8], pos: usize) -> Self {
18        Reader { buf, pos }
19    }
20
21    /// Bytes remaining.
22    pub fn remaining(&self) -> usize {
23        self.buf.len().saturating_sub(self.pos)
24    }
25
26    /// Current position relative to the start of `buf`.
27    pub fn pos(&self) -> usize {
28        self.pos
29    }
30
31    /// True when the cursor reached the end of the buffer.
32    pub fn at_end(&self) -> bool {
33        self.pos >= self.buf.len()
34    }
35
36    /// Read one unsigned little-endian integer of the given width.
37    pub fn uint<const N: usize>(&mut self) -> Result<u64, crate::error::ProtoError> {
38        if self.pos + N > self.buf.len() {
39            return Err(crate::error::ProtoError::Overrun {
40                need: N,
41                at: self.pos,
42                have: self.buf.len(),
43            });
44        }
45        let mut v: u64 = 0;
46        for i in 0..N {
47            v |= (self.buf[self.pos + i] as u64) << (8 * i);
48        }
49        self.pos += N;
50        Ok(v)
51    }
52
53    /// Read a single byte.
54    pub fn u8v(&mut self) -> Result<u8, crate::error::ProtoError> {
55        if self.pos >= self.buf.len() {
56            return Err(crate::error::ProtoError::Overrun {
57                need: 1,
58                at: self.pos,
59                have: self.buf.len(),
60            });
61        }
62        let v = self.buf[self.pos];
63        self.pos += 1;
64        Ok(v)
65    }
66
67    /// Skip forward by `n` bytes (clamped to the end).
68    pub fn skip(&mut self, n: usize) {
69        self.pos = (self.pos + n).min(self.buf.len());
70    }
71
72    /// Borrow the next `n` bytes without copying.
73    pub fn take(&mut self, n: usize) -> Result<&'a [u8], crate::error::ProtoError> {
74        if self.pos + n > self.buf.len() {
75            return Err(crate::error::ProtoError::Overrun {
76                need: n,
77                at: self.pos,
78                have: self.buf.len(),
79            });
80        }
81        let s = &self.buf[self.pos..self.pos + n];
82        self.pos += n;
83        Ok(s)
84    }
85
86    /// Read a NUL-terminated string honouring the SMB Unicode rules:
87    /// when `unicode` is set the cursor first skips one pad byte if the
88    /// current position is odd relative to `base`, then decodes UTF-16LE up
89    /// to and including the 16-bit terminator.
90    ///
91    /// `base` must be the absolute frame offset of `buf[0]`.
92    pub fn zstring(&mut self, unicode: bool, base: usize) -> String {
93        if unicode {
94            if (base + self.pos) & 1 != 0 && !self.at_end() {
95                self.pos += 1;
96            }
97            let mut units = Vec::new();
98            while self.pos + 1 < self.buf.len() {
99                let lo = self.buf[self.pos];
100                let hi = self.buf[self.pos + 1];
101                self.pos += 2;
102                let u = lo as u16 | ((hi as u16) << 8);
103                if u == 0 {
104                    break;
105                }
106                units.push(u);
107            }
108            String::from_utf16_lossy(&units)
109        } else {
110            let start = self.pos;
111            while self.pos < self.buf.len() && self.buf[self.pos] != 0 {
112                self.pos += 1;
113            }
114            let s = String::from_utf8_lossy(&self.buf[start..self.pos]).into_owned();
115            if self.pos < self.buf.len() {
116                self.pos += 1; // consume terminator
117            }
118            s
119        }
120    }
121
122    /// Read exactly `len` bytes as a lossy UTF-16LE or OEM string
123    /// (no terminator handling) — used for length-prefixed name fields.
124    pub fn fixed_string(&mut self, len: usize, unicode: bool) -> String {
125        let end = (self.pos + len).min(self.buf.len());
126        let raw = &self.buf[self.pos..end];
127        self.pos = end;
128        if unicode {
129            let units: Vec<u16> = raw
130                .chunks_exact(2)
131                .map(|c| c[0] as u16 | ((c[1] as u16) << 8))
132                .take_while(|&u| u != 0)
133                .collect();
134            String::from_utf16_lossy(&units)
135        } else {
136            String::from_utf8_lossy(raw.split(|&b| b == 0).next().unwrap_or(&[])).into_owned()
137        }
138    }
139}
140
141/// Writing cursor building an outgoing buffer.
142#[derive(Debug, Default)]
143pub struct Writer {
144    buf: Vec<u8>,
145    /// Absolute frame offset that will correspond to `buf[0]` on the wire.
146    base: usize,
147}
148
149impl Writer {
150    /// New writer whose first byte will sit at absolute frame offset `base`
151    /// (needed for Unicode parity padding decisions).
152    pub fn new(base: usize) -> Self {
153        Writer { buf: Vec::new(), base }
154    }
155
156    /// Number of bytes written so far.
157    pub fn len(&self) -> usize {
158        self.buf.len()
159    }
160
161    /// True when nothing has been written yet.
162    pub fn is_empty(&self) -> bool {
163        self.buf.is_empty()
164    }
165
166    /// Borrow the accumulated bytes.
167    pub fn into_inner(self) -> Vec<u8> {
168        self.buf
169    }
170
171    /// Append raw bytes.
172    pub fn raw(&mut self, data: &[u8]) {
173        self.buf.extend_from_slice(data);
174    }
175
176    /// Append one byte.
177    pub fn push(&mut self, v: u8) {
178        self.buf.push(v);
179    }
180
181    /// Append a little-endian `u16`.
182    pub fn push_u16(&mut self, v: u16) {
183        self.buf.extend_from_slice(&v.to_le_bytes());
184    }
185
186    /// Append a little-endian `u32`.
187    pub fn push_u32(&mut self, v: u32) {
188        self.buf.extend_from_slice(&v.to_le_bytes());
189    }
190
191    /// Append a little-endian `u64`.
192    pub fn push_u64(&mut self, v: u64) {
193        self.buf.extend_from_slice(&v.to_le_bytes());
194    }
195
196    /// Insert a pad byte so the current position becomes even relative to
197    /// [`Self::base`] (Unicode alignment requirement).
198    pub fn pad_to_parity(&mut self) {
199        if (self.base + self.buf.len()) & 1 != 0 {
200            self.buf.push(0);
201        }
202    }
203
204    /// Append a NUL-terminated string honouring the Unicode flag.
205    pub fn zstring(&mut self, s: &str, unicode: bool) {
206        if unicode {
207            self.pad_to_parity();
208            for unit in s.encode_utf16() {
209                self.buf.extend_from_slice(&unit.to_le_bytes());
210            }
211            self.buf.extend_from_slice(&[0, 0]);
212        } else {
213            self.buf.extend_from_slice(s.as_bytes());
214            self.buf.push(0);
215        }
216    }
217}