Skip to main content

oracledb_protocol/
wire.rs

1#![forbid(unsafe_code)]
2
3use crate::{ProtocolError, Result};
4
5pub const TNS_MAX_SHORT_LENGTH: usize = 252;
6pub const TNS_LONG_LENGTH_INDICATOR: u8 = 0xfe;
7pub const TNS_NULL_LENGTH_INDICATOR: u8 = 0xff;
8
9const MIB: usize = 1024 * 1024;
10
11/// Central resource policy for thin protocol decoding.
12///
13/// The values are deliberately collected in one copyable struct so connection,
14/// packet, TTC, object, vector, LOB, and notification decoders all share the
15/// same vocabulary for resource bounds. W1-T5.2 threads this policy through the
16/// current decoder call graph; this type is the single source of those limits.
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub struct ProtocolLimits {
19    /// Maximum encoded TNS packet size.
20    pub max_packet_bytes: usize,
21    /// Maximum decoded logical TTC frame/message size.
22    pub max_frame_bytes: usize,
23    /// Maximum cumulative bytes accepted for one server response.
24    pub max_response_bytes: usize,
25    /// Maximum number of columns in one describe/fetch shape.
26    pub max_columns: usize,
27    /// Maximum bind count in one execute/batch request.
28    pub max_binds: usize,
29    /// Maximum row count in one client-side batch operation.
30    pub max_batch_rows: usize,
31    /// Maximum recursive object/JSON nesting depth.
32    pub max_object_depth: usize,
33    /// Maximum element/member count in one decoded object/JSON container.
34    pub max_object_elements: usize,
35    /// Maximum VECTOR dimensions.
36    pub max_vector_dimensions: usize,
37    /// Maximum number of LOB chunks in one logical LOB operation.
38    pub max_lob_chunks: usize,
39    /// Maximum elements in generic length-prefixed wire collections.
40    pub max_length_prefixed_elements: usize,
41}
42
43impl Default for ProtocolLimits {
44    fn default() -> Self {
45        Self::DEFAULT
46    }
47}
48
49impl ProtocolLimits {
50    pub const DEFAULT: Self = Self {
51        max_packet_bytes: 16 * MIB,
52        max_frame_bytes: 16 * MIB,
53        max_response_bytes: 256 * MIB,
54        max_columns: 4096,
55        max_binds: 65_535,
56        max_batch_rows: 1_000_000,
57        max_object_depth: 256,
58        max_object_elements: 1_000_000,
59        max_vector_dimensions: 1_000_000,
60        max_lob_chunks: 1_000_000,
61        max_length_prefixed_elements: 1_000_000,
62    };
63
64    /// Validate caller-supplied limits before attaching them to a connection.
65    pub fn validate(self) -> Result<Self> {
66        for (name, value) in self.named_limits() {
67            if value == 0 {
68                return Err(ProtocolError::ResourceLimit {
69                    limit: name,
70                    observed: 0,
71                    maximum: 1,
72                });
73            }
74        }
75        if self.max_packet_bytes > self.max_frame_bytes {
76            return Err(ProtocolError::ResourceLimit {
77                limit: "packet_bytes",
78                observed: self.max_packet_bytes,
79                maximum: self.max_frame_bytes,
80            });
81        }
82        if self.max_frame_bytes > self.max_response_bytes {
83            return Err(ProtocolError::ResourceLimit {
84                limit: "frame_bytes",
85                observed: self.max_frame_bytes,
86                maximum: self.max_response_bytes,
87            });
88        }
89        Ok(self)
90    }
91
92    pub fn check_packet_bytes(&self, observed: usize) -> Result<()> {
93        self.check("packet_bytes", observed, self.max_packet_bytes)
94    }
95
96    pub fn check_frame_bytes(&self, observed: usize) -> Result<()> {
97        self.check("frame_bytes", observed, self.max_frame_bytes)
98    }
99
100    pub fn check_response_bytes(&self, observed: usize) -> Result<()> {
101        self.check("response_bytes", observed, self.max_response_bytes)
102    }
103
104    pub fn check_columns(&self, observed: usize) -> Result<()> {
105        self.check("columns", observed, self.max_columns)
106    }
107
108    pub fn check_binds(&self, observed: usize) -> Result<()> {
109        self.check("binds", observed, self.max_binds)
110    }
111
112    pub fn check_batch_rows(&self, observed: usize) -> Result<()> {
113        self.check("batch_rows", observed, self.max_batch_rows)
114    }
115
116    pub fn check_object_depth(&self, observed: usize) -> Result<()> {
117        self.check("object_depth", observed, self.max_object_depth)
118    }
119
120    pub fn check_object_elements(&self, observed: usize) -> Result<()> {
121        self.check("object_elements", observed, self.max_object_elements)
122    }
123
124    pub fn check_vector_dimensions(&self, observed: usize) -> Result<()> {
125        self.check("vector_dimensions", observed, self.max_vector_dimensions)
126    }
127
128    pub fn check_lob_chunks(&self, observed: usize) -> Result<()> {
129        self.check("lob_chunks", observed, self.max_lob_chunks)
130    }
131
132    pub fn check_length_prefixed_elements(&self, observed: usize) -> Result<()> {
133        self.check(
134            "length_prefixed_elements",
135            observed,
136            self.max_length_prefixed_elements,
137        )
138    }
139
140    fn check(&self, limit: &'static str, observed: usize, maximum: usize) -> Result<()> {
141        if observed <= maximum {
142            Ok(())
143        } else {
144            Err(ProtocolError::ResourceLimit {
145                limit,
146                observed,
147                maximum,
148            })
149        }
150    }
151
152    fn named_limits(&self) -> [(&'static str, usize); 11] {
153        [
154            ("packet_bytes", self.max_packet_bytes),
155            ("frame_bytes", self.max_frame_bytes),
156            ("response_bytes", self.max_response_bytes),
157            ("columns", self.max_columns),
158            ("binds", self.max_binds),
159            ("batch_rows", self.max_batch_rows),
160            ("object_depth", self.max_object_depth),
161            ("object_elements", self.max_object_elements),
162            ("vector_dimensions", self.max_vector_dimensions),
163            ("lob_chunks", self.max_lob_chunks),
164            (
165                "length_prefixed_elements",
166                self.max_length_prefixed_elements,
167            ),
168        ]
169    }
170}
171
172#[derive(Clone, Copy, Debug, Eq, PartialEq)]
173pub enum PacketLengthWidth {
174    Legacy16,
175    Large32,
176}
177
178#[derive(Clone, Debug, Default, Eq, PartialEq)]
179pub struct TtcWriter {
180    bytes: Vec<u8>,
181    seq_num: u8,
182}
183
184impl TtcWriter {
185    pub fn new() -> Self {
186        Self::default()
187    }
188
189    /// A writer whose backing buffer is preallocated to `capacity` bytes. A
190    /// `TtcWriter::new()` starts at zero capacity, so a payload built from many
191    /// small `write_*` pushes grows the `Vec` through several doublings — each a
192    /// separate heap allocation. Sizing the buffer once (to a small-payload
193    /// default or an exact known length) collapses those growth reallocs to a
194    /// single allocation. The written bytes are byte-identical either way; this
195    /// is a pure allocation optimization.
196    pub fn with_capacity(capacity: usize) -> Self {
197        Self {
198            bytes: Vec::with_capacity(capacity),
199            seq_num: 0,
200        }
201    }
202
203    pub fn into_bytes(self) -> Vec<u8> {
204        self.bytes
205    }
206
207    pub fn write_u8(&mut self, value: u8) {
208        self.bytes.push(value);
209    }
210
211    pub fn write_u16be(&mut self, value: u16) {
212        self.bytes.extend_from_slice(&value.to_be_bytes());
213    }
214
215    pub fn write_u16le(&mut self, value: u16) {
216        self.bytes.extend_from_slice(&value.to_le_bytes());
217    }
218
219    pub fn write_u32be(&mut self, value: u32) {
220        self.bytes.extend_from_slice(&value.to_be_bytes());
221    }
222
223    pub fn write_u64be(&mut self, value: u64) {
224        self.bytes.extend_from_slice(&value.to_be_bytes());
225    }
226
227    pub fn write_ub2(&mut self, value: u16) {
228        if value == 0 {
229            self.write_u8(0);
230        } else if value <= u16::from(u8::MAX) {
231            self.write_u8(1);
232            self.write_u8(value as u8);
233        } else {
234            self.write_u8(2);
235            self.write_u16be(value);
236        }
237    }
238
239    pub fn write_ub4(&mut self, value: u32) {
240        if value == 0 {
241            self.write_u8(0);
242        } else if value <= u32::from(u8::MAX) {
243            self.write_u8(1);
244            self.write_u8(value as u8);
245        } else if value <= u32::from(u16::MAX) {
246            self.write_u8(2);
247            self.write_u16be(value as u16);
248        } else {
249            self.write_u8(4);
250            self.write_u32be(value);
251        }
252    }
253
254    pub fn write_ub8(&mut self, value: u64) {
255        if value == 0 {
256            self.write_u8(0);
257        } else if value <= u64::from(u8::MAX) {
258            self.write_u8(1);
259            self.write_u8(value as u8);
260        } else if value <= u64::from(u16::MAX) {
261            self.write_u8(2);
262            self.write_u16be(value as u16);
263        } else if value <= u64::from(u32::MAX) {
264            self.write_u8(4);
265            self.write_u32be(value as u32);
266        } else {
267            self.write_u8(8);
268            self.write_u64be(value);
269        }
270    }
271
272    pub fn write_seq_num(&mut self) {
273        self.seq_num = self.seq_num.wrapping_add(1);
274        if self.seq_num == 0 {
275            self.seq_num = 1;
276        }
277        self.write_u8(self.seq_num);
278    }
279
280    pub fn write_raw(&mut self, value: &[u8]) {
281        self.bytes.extend_from_slice(value);
282    }
283
284    pub fn write_bytes_with_length(&mut self, value: &[u8]) -> Result<()> {
285        if value.len() <= TNS_MAX_SHORT_LENGTH {
286            self.write_u8(value.len() as u8);
287            self.write_raw(value);
288            return Ok(());
289        }
290        self.write_u8(TNS_LONG_LENGTH_INDICATOR);
291        for chunk in value.chunks(32_767) {
292            self.write_ub4(u32::try_from(chunk.len()).map_err(|_| {
293                ProtocolError::InvalidPacketLength {
294                    length: chunk.len(),
295                    minimum: 0,
296                }
297            })?);
298            self.write_raw(chunk);
299        }
300        self.write_ub4(0);
301        Ok(())
302    }
303
304    pub fn write_bytes_with_two_lengths(&mut self, value: Option<&[u8]>) -> Result<()> {
305        match value {
306            Some(bytes) => {
307                self.write_ub4(u32::try_from(bytes.len()).map_err(|_| {
308                    ProtocolError::InvalidPacketLength {
309                        length: bytes.len(),
310                        minimum: 0,
311                    }
312                })?);
313                if !bytes.is_empty() {
314                    self.write_bytes_with_length(bytes)?;
315                }
316            }
317            None => self.write_ub4(0),
318        }
319        Ok(())
320    }
321
322    pub fn write_str_two_lengths(&mut self, value: &str) -> Result<()> {
323        self.write_bytes_with_two_lengths(Some(value.as_bytes()))
324    }
325
326    /// Writes a 32-bit signed integer in Oracle universal (sign-magnitude)
327    /// format: a length byte whose high bit (`0x80`) is set for negatives,
328    /// followed by the big-endian magnitude bytes. Mirrors the reference
329    /// `WriteBuffer.write_sb4` (impl/base/buffer.pyx).
330    pub fn write_sb4(&mut self, value: i32) {
331        let (sign, magnitude) = if value < 0 {
332            (0x80u8, value.unsigned_abs())
333        } else {
334            (0u8, value as u32)
335        };
336        if magnitude == 0 {
337            self.write_u8(0);
338        } else if magnitude <= u32::from(u8::MAX) {
339            self.write_u8(1 | sign);
340            self.write_u8(magnitude as u8);
341        } else if magnitude <= u32::from(u16::MAX) {
342            self.write_u8(2 | sign);
343            self.write_u16be(magnitude as u16);
344        } else {
345            self.write_u8(4 | sign);
346            self.write_u32be(magnitude);
347        }
348    }
349
350    /// Writes a keyword/value pair (text and binary values plus a ub2 keyword)
351    /// as used by the AQ message-property extension list. Mirrors the reference
352    /// `WriteBuffer.write_keyword_value_pair` (impl/thin/packet.pyx:859).
353    pub fn write_keyword_value_pair(
354        &mut self,
355        text_value: Option<&[u8]>,
356        binary_value: Option<&[u8]>,
357        keyword: u16,
358    ) -> Result<()> {
359        self.write_bytes_with_two_lengths(text_value)?;
360        self.write_bytes_with_two_lengths(binary_value)?;
361        self.write_ub2(keyword);
362        Ok(())
363    }
364
365    pub fn write_function_code(&mut self, function_code: u8) {
366        self.write_u8(crate::thin::TNS_MSG_TYPE_FUNCTION);
367        self.write_u8(function_code);
368        self.write_seq_num();
369    }
370
371    pub fn write_function_code_with_seq(&mut self, function_code: u8, seq_num: u8) {
372        self.write_u8(crate::thin::TNS_MSG_TYPE_FUNCTION);
373        self.write_u8(function_code);
374        self.write_u8(seq_num);
375    }
376}
377
378/// The structural OOM-from-length invariant for every wire decoder.
379///
380/// A length/count field read from the wire can **never** drive an allocation
381/// larger than the bytes actually remaining in the current message buffer: you
382/// cannot have `N` elements if fewer than `N * min_bytes_per_elem` bytes remain.
383/// Every reader over an untrusted buffer (`TtcReader`, the OSON / DbObject /
384/// notification cursors, the VECTOR reader) implements this trait, and every
385/// count-driven `Vec::with_capacity` / `reserve` in the decoders routes through
386/// one of its two methods instead of trusting a raw `u16`/`u32`/`u64` count.
387///
388/// This closes the OOM-from-length bug class *by construction*: a new decoder
389/// physically cannot pre-allocate from a wire count without going through a
390/// bound, because the raw `Vec::with_capacity(count)` shape is the thing we
391/// audit against (see `docs/FUZZING.md`).
392///
393/// Two flavors, both anchored on [`remaining`](Self::remaining):
394///
395/// * [`alloc_count_checked`](Self::alloc_count_checked) — fail *closed* early:
396///   returns an `Err` if the declared count cannot possibly fit, before any
397///   allocation. Use where an oversized count is unambiguously malformed.
398/// * [`with_capacity_bounded`](Self::with_capacity_bounded) — cap *the
399///   pre-allocation* at what the buffer could hold while still returning a
400///   normal growable `Vec`. Use where the loop body itself fails closed on the
401///   first truncated element read; legitimate large payloads keep working
402///   because the cap equals the honest count whenever the bytes are really
403///   there.
404pub trait BoundedReader {
405    /// Bytes still unread in the current message buffer. The ceiling on any
406    /// count-driven allocation.
407    fn remaining(&self) -> usize;
408
409    /// Resource policy attached to this decoder. Readers that have not yet
410    /// grown a configurable policy surface use the validated defaults.
411    fn protocol_limits(&self) -> ProtocolLimits {
412        ProtocolLimits::DEFAULT
413    }
414
415    /// Validate a server-declared element `count` against the buffer: a run of
416    /// `count` elements must carry at least `count * min_bytes_per_elem` bytes,
417    /// so a count whose minimum byte footprint exceeds [`Self::remaining`] is a lie.
418    /// Returns the (unchanged) `count` when it fits, or a fail-closed
419    /// [`ProtocolError::TtcDecode`] otherwise — never a panic, never an OOM.
420    ///
421    /// `min_bytes_per_elem` is the *minimum* on-wire size of one element (e.g.
422    /// 4 for a `u32` index, 8 for an `f64`, 1 for a length-prefixed field whose
423    /// shortest legal form is a single length byte). A zero is treated as 1.
424    fn alloc_count_checked(&self, count: usize, min_bytes_per_elem: usize) -> Result<usize> {
425        self.protocol_limits()
426            .check_length_prefixed_elements(count)?;
427        let per_elem = min_bytes_per_elem.max(1);
428        match count.checked_mul(per_elem) {
429            Some(needed) if needed <= self.remaining() => Ok(count),
430            _ => Err(ProtocolError::TtcDecode(
431                "declared element count exceeds remaining buffer",
432            )),
433        }
434    }
435
436    /// Pre-size a `Vec` for `count` elements *without* trusting `count`: the
437    /// reserved capacity is capped at `remaining() / min_bytes_per_elem`, the
438    /// largest number of elements the buffer could actually hold. The returned
439    /// `Vec` is a normal growable `Vec`, so a legitimately large payload (where
440    /// `count` really fits) is pre-sized to the honest count, and a streamed /
441    /// chunked field that grows past the initial buffer still appends correctly
442    /// — the cap only governs the *speculative* up-front reservation.
443    fn with_capacity_bounded<T>(&self, count: usize, min_bytes_per_elem: usize) -> Vec<T> {
444        let per_elem = min_bytes_per_elem.max(1);
445        Vec::with_capacity(count.min(self.remaining() / per_elem))
446    }
447
448    /// Policy-aware form of [`with_capacity_bounded`](Self::with_capacity_bounded):
449    /// the caller supplies the resource family check, then the speculative
450    /// allocation is still capped by the remaining buffer.
451    fn with_capacity_limited<T, F>(
452        &self,
453        count: usize,
454        min_bytes_per_elem: usize,
455        check: F,
456    ) -> Result<Vec<T>>
457    where
458        F: FnOnce(&ProtocolLimits, usize) -> Result<()>,
459    {
460        check(&self.protocol_limits(), count)?;
461        Ok(self.with_capacity_bounded(count, min_bytes_per_elem))
462    }
463}
464
465#[derive(Clone, Debug)]
466pub struct TtcReader<'a> {
467    bytes: &'a [u8],
468    pos: usize,
469    limits: ProtocolLimits,
470}
471
472impl BoundedReader for TtcReader<'_> {
473    fn remaining(&self) -> usize {
474        TtcReader::remaining(self)
475    }
476
477    fn protocol_limits(&self) -> ProtocolLimits {
478        self.limits
479    }
480}
481
482/// Outcome of [`TtcReader::read_bytes_borrowed`]: a borrowed run of the wire
483/// buffer for the common contiguous short-value case, an owned fallback for the
484/// non-contiguous chunked long form, or NULL.
485#[derive(Clone, Debug, PartialEq, Eq)]
486pub enum BorrowedBytes<'a> {
487    /// SQL NULL (length byte `0` or `0xff`).
488    Null,
489    /// A contiguous run borrowed directly from the buffer (zero-copy).
490    Slice(&'a [u8]),
491    /// The chunked long form (`0xfe`), reassembled into an owned `Vec` because
492    /// the chunks are not contiguous on the wire. The rare path.
493    Chunked(Vec<u8>),
494}
495
496impl<'a> TtcReader<'a> {
497    pub fn new(bytes: &'a [u8]) -> Self {
498        Self {
499            bytes,
500            pos: 0,
501            limits: ProtocolLimits::DEFAULT,
502        }
503    }
504
505    pub fn with_limits(bytes: &'a [u8], limits: ProtocolLimits) -> Result<Self> {
506        let limits = limits.validate()?;
507        limits.check_frame_bytes(bytes.len())?;
508        limits.check_response_bytes(bytes.len())?;
509        Ok(Self {
510            bytes,
511            pos: 0,
512            limits,
513        })
514    }
515
516    pub fn limits(&self) -> ProtocolLimits {
517        self.limits
518    }
519
520    pub fn remaining(&self) -> usize {
521        self.bytes.len().saturating_sub(self.pos)
522    }
523
524    pub fn position(&self) -> usize {
525        self.pos
526    }
527
528    pub fn remaining_slice(&self) -> &[u8] {
529        &self.bytes[self.pos.min(self.bytes.len())..]
530    }
531
532    pub fn peek_u8(&self) -> Result<u8> {
533        self.bytes
534            .get(self.pos)
535            .copied()
536            .ok_or(ProtocolError::TtcDecode("missing u8"))
537    }
538
539    pub fn read_u8(&mut self) -> Result<u8> {
540        let value = *self
541            .bytes
542            .get(self.pos)
543            .ok_or(ProtocolError::TtcDecode("missing u8"))?;
544        self.pos += 1;
545        Ok(value)
546    }
547
548    pub fn read_i8(&mut self) -> Result<i8> {
549        Ok(self.read_u8()? as i8)
550    }
551
552    pub fn read_u16be(&mut self) -> Result<u16> {
553        let bytes = self.read_raw(2)?;
554        Ok(u16::from_be_bytes(
555            bytes
556                .try_into()
557                .map_err(|_| ProtocolError::TtcDecode("invalid u16"))?,
558        ))
559    }
560
561    pub fn read_u16le(&mut self) -> Result<u16> {
562        let bytes = self.read_raw(2)?;
563        Ok(u16::from_le_bytes(
564            bytes
565                .try_into()
566                .map_err(|_| ProtocolError::TtcDecode("invalid u16"))?,
567        ))
568    }
569
570    pub fn read_u32be(&mut self) -> Result<u32> {
571        let bytes = self.read_raw(4)?;
572        Ok(u32::from_be_bytes(
573            bytes
574                .try_into()
575                .map_err(|_| ProtocolError::TtcDecode("invalid u32"))?,
576        ))
577    }
578
579    pub fn read_raw(&mut self, len: usize) -> Result<&'a [u8]> {
580        self.limits.check_response_bytes(len)?;
581        let end = self
582            .pos
583            .checked_add(len)
584            .ok_or(ProtocolError::TtcDecode("read offset overflow"))?;
585        let bytes = self
586            .bytes
587            .get(self.pos..end)
588            .ok_or(ProtocolError::TtcDecode("truncated TTC payload"))?;
589        self.pos = end;
590        Ok(bytes)
591    }
592
593    pub fn skip(&mut self, len: usize) -> Result<()> {
594        self.read_raw(len).map(|_| ())
595    }
596
597    pub fn read_ub2(&mut self) -> Result<u16> {
598        let len = self.read_u8()?;
599        match len {
600            0 => Ok(0),
601            1 => Ok(u16::from(self.read_u8()?)),
602            2 => self.read_u16be(),
603            _ => Err(ProtocolError::TtcDecode("invalid ub2 length")),
604        }
605    }
606
607    pub fn read_ub4(&mut self) -> Result<u32> {
608        let len = self.read_u8()?;
609        if len == 0 {
610            return Ok(0);
611        }
612        if len > 4 {
613            return Err(ProtocolError::TtcDecode("invalid ub4 length"));
614        }
615        let mut value = 0u32;
616        for byte in self.read_raw(usize::from(len))? {
617            value = (value << 8) | u32::from(*byte);
618        }
619        Ok(value)
620    }
621
622    pub fn read_sb4(&mut self) -> Result<i32> {
623        let len = self.read_u8()?;
624        let is_negative = len & 0x80 != 0;
625        let len = len & 0x7f;
626        if len == 0 {
627            return Ok(0);
628        }
629        if len > 4 {
630            return Err(ProtocolError::TtcDecode("invalid sb4 length"));
631        }
632        // Accumulate in the unsigned width and reinterpret as signed: a server
633        // can send four bytes whose high bit is set (so the signed value is
634        // i32::MIN) and flag the length as negative. Negating i32::MIN — or even
635        // the intermediate `value << 8` — would overflow and panic under the
636        // debug/overflow-checked fuzz build. `wrapping_neg` matches the
637        // reference C decoder's two's-complement behavior and never panics.
638        let mut value = 0u32;
639        for byte in self.read_raw(usize::from(len))? {
640            value = (value << 8) | u32::from(*byte);
641        }
642        let value = value as i32;
643        Ok(if is_negative {
644            value.wrapping_neg()
645        } else {
646            value
647        })
648    }
649
650    pub fn read_sb8(&mut self) -> Result<i64> {
651        let len = self.read_u8()?;
652        let is_negative = len & 0x80 != 0;
653        let len = len & 0x7f;
654        if len == 0 {
655            return Ok(0);
656        }
657        if len > 8 {
658            return Err(ProtocolError::TtcDecode("invalid sb8 length"));
659        }
660        // See `read_sb4`: unsigned accumulation plus `wrapping_neg` avoids the
661        // i64::MIN negate-overflow panic on adversarial input.
662        let mut value = 0u64;
663        for byte in self.read_raw(usize::from(len))? {
664            value = (value << 8) | u64::from(*byte);
665        }
666        let value = value as i64;
667        Ok(if is_negative {
668            value.wrapping_neg()
669        } else {
670            value
671        })
672    }
673
674    pub fn read_ub8(&mut self) -> Result<u64> {
675        let len = self.read_u8()?;
676        if len == 0 {
677            return Ok(0);
678        }
679        if len > 8 {
680            return Err(ProtocolError::TtcDecode("invalid ub8 length"));
681        }
682        let mut value = 0u64;
683        for byte in self.read_raw(usize::from(len))? {
684            value = (value << 8) | u64::from(*byte);
685        }
686        Ok(value)
687    }
688
689    /// Zero-copy companion to [`read_bytes`](Self::read_bytes) for the borrowed
690    /// fetch path. The common short-value form (length byte 1..=253) is a single
691    /// contiguous run in the buffer, so it is returned as a borrowed slice with
692    /// no allocation. The chunked long form (`0xfe`) is *not* contiguous on the
693    /// wire (it is a sequence of length-prefixed chunks), so it cannot be
694    /// borrowed and falls back to an owned `Vec` — the rare path. `0`/`0xff`
695    /// signal SQL NULL.
696    ///
697    /// Consumes exactly the same number of bytes as `read_bytes` for every
698    /// input, so the two are interchangeable mid-stream.
699    pub fn read_bytes_borrowed(&mut self) -> Result<BorrowedBytes<'a>> {
700        let len = self.read_u8()?;
701        if len == TNS_LONG_LENGTH_INDICATOR {
702            let mut out = Vec::new();
703            let mut chunks = 0usize;
704            let mut total = 0usize;
705            loop {
706                let chunk_len = self.read_ub4()?;
707                if chunk_len == 0 {
708                    break;
709                }
710                chunks = chunks.checked_add(1).ok_or(ProtocolError::ResourceLimit {
711                    limit: "lob_chunks",
712                    observed: usize::MAX,
713                    maximum: self.limits.max_lob_chunks,
714                })?;
715                self.limits.check_lob_chunks(chunks)?;
716                let chunk_len =
717                    usize::try_from(chunk_len).map_err(|_| ProtocolError::InvalidPacketLength {
718                        length: usize::MAX,
719                        minimum: 0,
720                    })?;
721                total = total
722                    .checked_add(chunk_len)
723                    .ok_or(ProtocolError::ResourceLimit {
724                        limit: "response_bytes",
725                        observed: usize::MAX,
726                        maximum: self.limits.max_response_bytes,
727                    })?;
728                self.limits.check_response_bytes(total)?;
729                let chunk = self.read_raw(chunk_len)?;
730                out.extend_from_slice(chunk);
731            }
732            Ok(BorrowedBytes::Chunked(out))
733        } else if len == 0 || len == TNS_NULL_LENGTH_INDICATOR {
734            Ok(BorrowedBytes::Null)
735        } else {
736            self.limits.check_response_bytes(usize::from(len))?;
737            Ok(BorrowedBytes::Slice(self.read_raw(usize::from(len))?))
738        }
739    }
740
741    /// Advance past one length-prefixed TTC byte field (short, NULL, or chunked
742    /// long form) **without allocating** — the zero-copy skip used by the
743    /// borrowed fetch offset-capture pass. Consumes exactly the bytes
744    /// [`read_bytes`](Self::read_bytes) would.
745    pub fn skip_bytes_field(&mut self) -> Result<()> {
746        let len = self.read_u8()?;
747        if len == TNS_LONG_LENGTH_INDICATOR {
748            let mut chunks = 0usize;
749            let mut total = 0usize;
750            loop {
751                let chunk_len = self.read_ub4()?;
752                if chunk_len == 0 {
753                    break;
754                }
755                chunks = chunks.checked_add(1).ok_or(ProtocolError::ResourceLimit {
756                    limit: "lob_chunks",
757                    observed: usize::MAX,
758                    maximum: self.limits.max_lob_chunks,
759                })?;
760                self.limits.check_lob_chunks(chunks)?;
761                let chunk_len =
762                    usize::try_from(chunk_len).map_err(|_| ProtocolError::InvalidPacketLength {
763                        length: usize::MAX,
764                        minimum: 0,
765                    })?;
766                total = total
767                    .checked_add(chunk_len)
768                    .ok_or(ProtocolError::ResourceLimit {
769                        limit: "response_bytes",
770                        observed: usize::MAX,
771                        maximum: self.limits.max_response_bytes,
772                    })?;
773                self.limits.check_response_bytes(total)?;
774                self.skip(chunk_len)?;
775            }
776            Ok(())
777        } else if len == 0 || len == TNS_NULL_LENGTH_INDICATOR {
778            Ok(())
779        } else {
780            self.limits.check_response_bytes(usize::from(len))?;
781            self.skip(usize::from(len))
782        }
783    }
784
785    pub fn read_bytes(&mut self) -> Result<Option<Vec<u8>>> {
786        let len = self.read_u8()?;
787        if len == TNS_LONG_LENGTH_INDICATOR {
788            let mut out = Vec::new();
789            let mut chunks = 0usize;
790            let mut total = 0usize;
791            loop {
792                let chunk_len = self.read_ub4()?;
793                if chunk_len == 0 {
794                    break;
795                }
796                chunks = chunks.checked_add(1).ok_or(ProtocolError::ResourceLimit {
797                    limit: "lob_chunks",
798                    observed: usize::MAX,
799                    maximum: self.limits.max_lob_chunks,
800                })?;
801                self.limits.check_lob_chunks(chunks)?;
802                let chunk_len =
803                    usize::try_from(chunk_len).map_err(|_| ProtocolError::InvalidPacketLength {
804                        length: usize::MAX,
805                        minimum: 0,
806                    })?;
807                total = total
808                    .checked_add(chunk_len)
809                    .ok_or(ProtocolError::ResourceLimit {
810                        limit: "response_bytes",
811                        observed: usize::MAX,
812                        maximum: self.limits.max_response_bytes,
813                    })?;
814                self.limits.check_response_bytes(total)?;
815                let chunk = self.read_raw(chunk_len)?;
816                out.extend_from_slice(chunk);
817            }
818            Ok(Some(out))
819        } else if len == 0 || len == TNS_NULL_LENGTH_INDICATOR {
820            Ok(None)
821        } else {
822            self.limits.check_response_bytes(usize::from(len))?;
823            Ok(Some(self.read_raw(usize::from(len))?.to_vec()))
824        }
825    }
826
827    pub fn read_bytes_with_length(&mut self) -> Result<Option<Vec<u8>>> {
828        let len =
829            usize::try_from(self.read_ub4()?).map_err(|_| ProtocolError::InvalidPacketLength {
830                length: usize::MAX,
831                minimum: 0,
832            })?;
833        self.limits.check_response_bytes(len)?;
834        if len == 0 {
835            return Ok(None);
836        }
837        let value_start = self.pos;
838        match self.read_bytes() {
839            Ok(Some(bytes)) if bytes.len() == len => Ok(Some(bytes)),
840            Ok(_) | Err(_) => {
841                self.pos = value_start;
842                Ok(Some(self.read_raw(len)?.to_vec()))
843            }
844        }
845    }
846
847    pub fn read_string_with_length(&mut self) -> Result<Option<String>> {
848        let Some(bytes) = self.read_bytes_with_length()? else {
849            return Ok(None);
850        };
851        String::from_utf8(bytes)
852            .map(Some)
853            .map_err(|_| ProtocolError::TtcDecode("server sent non-UTF8 string"))
854    }
855
856    pub fn read_string(&mut self) -> Result<Option<String>> {
857        let Some(bytes) = self.read_bytes()? else {
858            return Ok(None);
859        };
860        String::from_utf8(bytes)
861            .map(Some)
862            .map_err(|_| ProtocolError::TtcDecode("server sent non-UTF8 string"))
863    }
864}
865
866pub fn encode_packet(
867    packet_type: u8,
868    packet_flags: u8,
869    data_flags: Option<u16>,
870    payload: &[u8],
871    width: PacketLengthWidth,
872) -> Result<Vec<u8>> {
873    let data_flags_len = usize::from(data_flags.is_some()) * 2;
874    let length = crate::packet::TNS_HEADER_LEN + data_flags_len + payload.len();
875    let mut out = Vec::with_capacity(length);
876    match width {
877        PacketLengthWidth::Legacy16 => {
878            let wire_length =
879                u16::try_from(length).map_err(|_| ProtocolError::PacketTooLarge { length })?;
880            out.extend_from_slice(&wire_length.to_be_bytes());
881            out.extend_from_slice(&0u16.to_be_bytes());
882        }
883        PacketLengthWidth::Large32 => {
884            let wire_length =
885                u32::try_from(length).map_err(|_| ProtocolError::PacketTooLarge { length })?;
886            out.extend_from_slice(&wire_length.to_be_bytes());
887        }
888    }
889    out.push(packet_type);
890    out.push(packet_flags);
891    out.extend_from_slice(&0u16.to_be_bytes());
892    if let Some(flags) = data_flags {
893        out.extend_from_slice(&flags.to_be_bytes());
894    }
895    out.extend_from_slice(payload);
896    Ok(out)
897}
898
899#[cfg(test)]
900mod tests {
901    use super::*;
902
903    fn assert_resource_limit(
904        result: Result<()>,
905        expected_limit: &'static str,
906        expected_observed: usize,
907        expected_maximum: usize,
908    ) {
909        assert!(
910            matches!(
911                result,
912                Err(ProtocolError::ResourceLimit {
913                    limit,
914                    observed,
915                    maximum,
916                }) if limit == expected_limit
917                    && observed == expected_observed
918                    && maximum == expected_maximum
919            ),
920            "expected ResourceLimit {{ limit: {expected_limit}, observed: {expected_observed}, maximum: {expected_maximum} }}"
921        );
922    }
923
924    #[test]
925    fn protocol_limits_default_names_every_resource_family() {
926        let limits = ProtocolLimits::default()
927            .validate()
928            .expect("valid defaults");
929        assert_eq!(limits, ProtocolLimits::DEFAULT);
930        let names: Vec<&'static str> = limits
931            .named_limits()
932            .into_iter()
933            .map(|(name, value)| {
934                assert!(value > 0, "{name} must be non-zero");
935                name
936            })
937            .collect();
938        assert_eq!(
939            names,
940            vec![
941                "packet_bytes",
942                "frame_bytes",
943                "response_bytes",
944                "columns",
945                "binds",
946                "batch_rows",
947                "object_depth",
948                "object_elements",
949                "vector_dimensions",
950                "lob_chunks",
951                "length_prefixed_elements",
952            ]
953        );
954    }
955
956    #[test]
957    fn protocol_limits_check_helpers_return_typed_resource_limit_errors() {
958        let limits = ProtocolLimits {
959            max_packet_bytes: 8,
960            max_frame_bytes: 16,
961            max_response_bytes: 32,
962            max_columns: 2,
963            max_binds: 3,
964            max_batch_rows: 4,
965            max_object_depth: 5,
966            max_object_elements: 6,
967            max_vector_dimensions: 7,
968            max_lob_chunks: 8,
969            max_length_prefixed_elements: 9,
970        }
971        .validate()
972        .expect("valid test limits");
973
974        limits.check_packet_bytes(8).expect("boundary accepted");
975        limits.check_frame_bytes(16).expect("boundary accepted");
976        limits.check_response_bytes(32).expect("boundary accepted");
977        limits.check_columns(2).expect("boundary accepted");
978        limits.check_binds(3).expect("boundary accepted");
979        limits.check_batch_rows(4).expect("boundary accepted");
980        limits.check_object_depth(5).expect("boundary accepted");
981        limits.check_object_elements(6).expect("boundary accepted");
982        limits
983            .check_vector_dimensions(7)
984            .expect("boundary accepted");
985        limits.check_lob_chunks(8).expect("boundary accepted");
986        limits
987            .check_length_prefixed_elements(9)
988            .expect("boundary accepted");
989
990        assert_resource_limit(limits.check_packet_bytes(9), "packet_bytes", 9, 8);
991        assert_resource_limit(limits.check_frame_bytes(17), "frame_bytes", 17, 16);
992        assert_resource_limit(limits.check_response_bytes(33), "response_bytes", 33, 32);
993        assert_resource_limit(limits.check_columns(3), "columns", 3, 2);
994        assert_resource_limit(limits.check_binds(4), "binds", 4, 3);
995        assert_resource_limit(limits.check_batch_rows(5), "batch_rows", 5, 4);
996        assert_resource_limit(limits.check_object_depth(6), "object_depth", 6, 5);
997        assert_resource_limit(limits.check_object_elements(7), "object_elements", 7, 6);
998        assert_resource_limit(limits.check_vector_dimensions(8), "vector_dimensions", 8, 7);
999        assert_resource_limit(limits.check_lob_chunks(9), "lob_chunks", 9, 8);
1000        assert_resource_limit(
1001            limits.check_length_prefixed_elements(10),
1002            "length_prefixed_elements",
1003            10,
1004            9,
1005        );
1006    }
1007
1008    #[test]
1009    fn protocol_limits_validate_rejects_zero_and_inverted_byte_hierarchy() {
1010        let zero_columns = ProtocolLimits {
1011            max_columns: 0,
1012            ..ProtocolLimits::DEFAULT
1013        };
1014        assert!(matches!(
1015            zero_columns.validate(),
1016            Err(ProtocolError::ResourceLimit {
1017                limit: "columns",
1018                observed: 0,
1019                maximum: 1,
1020            })
1021        ));
1022
1023        let packet_larger_than_frame = ProtocolLimits {
1024            max_packet_bytes: 17,
1025            max_frame_bytes: 16,
1026            ..ProtocolLimits::DEFAULT
1027        };
1028        assert!(matches!(
1029            packet_larger_than_frame.validate(),
1030            Err(ProtocolError::ResourceLimit {
1031                limit: "packet_bytes",
1032                observed: 17,
1033                maximum: 16,
1034            })
1035        ));
1036
1037        let frame_larger_than_response = ProtocolLimits {
1038            max_packet_bytes: 16,
1039            max_frame_bytes: 33,
1040            max_response_bytes: 32,
1041            ..ProtocolLimits::DEFAULT
1042        };
1043        assert!(matches!(
1044            frame_larger_than_response.validate(),
1045            Err(ProtocolError::ResourceLimit {
1046                limit: "frame_bytes",
1047                observed: 33,
1048                maximum: 32,
1049            })
1050        ));
1051    }
1052
1053    #[test]
1054    fn ttc_reader_with_limits_rejects_oversized_raw_reads() {
1055        let limits = ProtocolLimits {
1056            max_packet_bytes: 4,
1057            max_frame_bytes: 4,
1058            max_response_bytes: 4,
1059            ..ProtocolLimits::DEFAULT
1060        };
1061        let mut reader = TtcReader::with_limits(&[1, 2, 3, 4], limits).expect("valid limits");
1062        assert!(matches!(
1063            reader.read_raw(5),
1064            Err(ProtocolError::ResourceLimit {
1065                limit: "response_bytes",
1066                observed: 5,
1067                maximum: 4,
1068            })
1069        ));
1070    }
1071
1072    #[test]
1073    fn ttc_reader_with_limits_rejects_too_many_lob_chunks() {
1074        let limits = ProtocolLimits {
1075            max_lob_chunks: 1,
1076            ..ProtocolLimits::DEFAULT
1077        };
1078        let bytes = [TNS_LONG_LENGTH_INDICATOR, 1, 1, b'a', 1, 1, b'b', 0];
1079        let mut reader = TtcReader::with_limits(&bytes, limits).expect("valid limits");
1080        assert!(matches!(
1081            reader.read_bytes(),
1082            Err(ProtocolError::ResourceLimit {
1083                limit: "lob_chunks",
1084                observed: 2,
1085                maximum: 1,
1086            })
1087        ));
1088    }
1089
1090    // --- BoundedReader invariant (l2p) -----------------------------------
1091    // A length/count field read from the wire can NEVER drive an allocation
1092    // larger than the bytes actually remaining in the buffer. These tests pin
1093    // both flavors of the bounded-allocation primitive: the early-erroring
1094    // `alloc_count_checked` and the cap-and-grow `with_capacity_bounded`.
1095
1096    #[test]
1097    fn alloc_count_checked_errs_when_count_exceeds_remaining() {
1098        // 4 bytes left in the buffer, but a declared count of ~4 billion 8-byte
1099        // elements. The honest minimum is 8 bytes per element, so the claim is
1100        // a lie and must fail closed rather than reserving ~32 GB.
1101        let bytes = [0u8; 4];
1102        let reader = TtcReader::new(&bytes);
1103        assert!(reader.alloc_count_checked(u32::MAX as usize, 8).is_err());
1104        // count * min_bytes that overflows usize must also fail closed.
1105        assert!(reader.alloc_count_checked(usize::MAX, 8).is_err());
1106    }
1107
1108    #[test]
1109    fn alloc_count_checked_ok_when_count_fits() {
1110        // 16 bytes remaining, two 8-byte elements declared: legitimate.
1111        let bytes = [0u8; 16];
1112        let reader = TtcReader::new(&bytes);
1113        assert_eq!(
1114            reader.alloc_count_checked(2, 8).expect("fits"),
1115            2,
1116            "a count whose bytes fit must pass through unchanged"
1117        );
1118        // A zero-minimum element size is treated as 1 byte (defensive) and a
1119        // zero count is always fine.
1120        assert_eq!(reader.alloc_count_checked(0, 0).expect("zero"), 0);
1121    }
1122
1123    #[test]
1124    fn with_capacity_bounded_caps_preallocation_but_still_grows() {
1125        // 8 bytes remaining; a hostile count of ~4 billion 4-byte elements.
1126        let bytes = [0u8; 8];
1127        let reader = TtcReader::new(&bytes);
1128        let v: Vec<u32> = reader.with_capacity_bounded(u32::MAX as usize, 4);
1129        // The pre-allocation is capped at remaining()/elem = 8/4 = 2, NOT 4e9.
1130        assert_eq!(
1131            v.capacity(),
1132            2,
1133            "pre-allocation must be capped by remaining"
1134        );
1135        // But the vec is still a normal growable Vec: pushing past the cap is
1136        // fine (legitimate large payloads keep working as chunks arrive).
1137        let mut v = v;
1138        for i in 0..100u32 {
1139            v.push(i);
1140        }
1141        assert_eq!(v.len(), 100);
1142    }
1143
1144    #[test]
1145    fn with_capacity_bounded_uses_full_count_when_buffer_is_large() {
1146        // 400 bytes remaining, 10 four-byte elements: the real count fits, so
1147        // the pre-allocation is the honest count, not an arbitrary small cap.
1148        let bytes = [0u8; 400];
1149        let reader = TtcReader::new(&bytes);
1150        let v: Vec<u32> = reader.with_capacity_bounded(10, 4);
1151        assert_eq!(v.capacity(), 10);
1152    }
1153
1154    // Regression (w6-fuzz, query_response target): a negative-flagged sb4/sb8
1155    // whose magnitude is i32::MIN / i64::MIN made `-value` overflow and panic
1156    // ("attempt to negate with overflow") under the overflow-checked fuzz
1157    // build. `read_sb4`/`read_sb8` must now wrap instead of panicking.
1158    #[test]
1159    fn sb4_sb8_negate_overflow_does_not_panic() {
1160        // len byte 0x84 => negative, 4 bytes; value bytes 80 00 00 00 => i32::MIN.
1161        let bytes = [0x84u8, 0x80, 0x00, 0x00, 0x00];
1162        let mut reader = TtcReader::new(&bytes);
1163        assert_eq!(reader.read_sb4().expect("sb4 must not panic"), i32::MIN);
1164
1165        // len byte 0x88 => negative, 8 bytes; 80 00.. => i64::MIN.
1166        let bytes8 = [0x88u8, 0x80, 0, 0, 0, 0, 0, 0, 0];
1167        let mut reader8 = TtcReader::new(&bytes8);
1168        assert_eq!(reader8.read_sb8().expect("sb8 must not panic"), i64::MIN);
1169    }
1170
1171    // Round-trip ordinary signed values to confirm the unsigned-accumulation
1172    // rewrite did not change behavior for the common range.
1173    #[test]
1174    fn sb4_decodes_representative_values() {
1175        // Hand-encoded sign-magnitude: len|0x80 for negatives.
1176        let cases: [(&[u8], i32); 4] = [
1177            (&[0x00], 0),
1178            (&[0x01, 0x2a], 42),
1179            (&[0x81, 0x2a], -42),
1180            (&[0x02, 0x01, 0x00], 256),
1181        ];
1182        for (bytes, expected) in cases {
1183            let mut reader = TtcReader::new(bytes);
1184            assert_eq!(
1185                reader.read_sb4().expect("sb4 decode"),
1186                expected,
1187                "{bytes:?}"
1188            );
1189        }
1190    }
1191
1192    #[test]
1193    fn ub4_round_trips_representative_values() {
1194        for value in [0, 1, 255, 256, 65_535, 65_536, u32::MAX] {
1195            let mut writer = TtcWriter::new();
1196            writer.write_ub4(value);
1197            let bytes = writer.into_bytes();
1198            let mut reader = TtcReader::new(&bytes);
1199            assert_eq!(reader.read_ub4().expect("ub4 should decode"), value);
1200            assert_eq!(reader.remaining(), 0);
1201        }
1202    }
1203
1204    // `read_bytes_borrowed` must borrow the contiguous short-value bytes
1205    // directly out of the buffer (the zero-copy hot path), signal `Null` for
1206    // 0/0xff length, and fall back to an owned `Chunked` Vec for the
1207    // 0xfe long-value form (which is not contiguous on the wire). The borrowed
1208    // slice must equal what `read_bytes` would return, and consume exactly the
1209    // same number of bytes.
1210    #[test]
1211    fn read_bytes_borrowed_borrows_short_values_and_owns_chunked() {
1212        // Short value: length byte 3 + "abc".
1213        let short = [0x03u8, b'a', b'b', b'c'];
1214        let mut reader = TtcReader::new(&short);
1215        let borrowed = reader.read_bytes_borrowed().expect("short decode");
1216        assert!(matches!(borrowed, BorrowedBytes::Slice(slice) if slice == b"abc"));
1217        assert_eq!(reader.remaining(), 0);
1218
1219        // NULL value: 0xff.
1220        let null = [TNS_NULL_LENGTH_INDICATOR];
1221        let mut reader = TtcReader::new(&null);
1222        assert!(matches!(
1223            reader.read_bytes_borrowed().expect("null decode"),
1224            BorrowedBytes::Null
1225        ));
1226
1227        // Zero-length value: 0x00 (also NULL in TTC).
1228        let zero = [0x00u8];
1229        let mut reader = TtcReader::new(&zero);
1230        assert!(matches!(
1231            reader.read_bytes_borrowed().expect("zero decode"),
1232            BorrowedBytes::Null
1233        ));
1234
1235        // Long/chunked value: 0xfe then ub4 chunk lengths terminated by 0.
1236        let mut writer = TtcWriter::new();
1237        writer
1238            .write_bytes_with_length(&vec![0x5au8; 600]) // forces the 0xfe chunked form
1239            .expect("chunked encode");
1240        let long = writer.into_bytes();
1241        let mut reader = TtcReader::new(&long);
1242        let expected = vec![0x5au8; 600];
1243        let borrowed = reader.read_bytes_borrowed().expect("chunked decode");
1244        assert!(matches!(&borrowed, BorrowedBytes::Chunked(bytes) if bytes == &expected));
1245        assert_eq!(reader.remaining(), 0);
1246    }
1247
1248    #[test]
1249    fn bytes_with_length_accepts_nested_ttc_bytes() {
1250        let mut writer = TtcWriter::new();
1251        writer
1252            .write_bytes_with_two_lengths(Some(b"abc"))
1253            .expect("bytes should encode");
1254        let bytes = writer.into_bytes();
1255        let mut reader = TtcReader::new(&bytes);
1256        assert_eq!(
1257            reader
1258                .read_bytes_with_length()
1259                .expect("bytes should decode"),
1260            Some(b"abc".to_vec())
1261        );
1262        assert_eq!(reader.remaining(), 0);
1263    }
1264
1265    #[test]
1266    fn bytes_with_length_accepts_direct_payload_bytes() {
1267        let bytes = [1, 3, b'a', b'b', b'c'];
1268        let mut reader = TtcReader::new(&bytes);
1269        assert_eq!(
1270            reader
1271                .read_bytes_with_length()
1272                .expect("bytes should decode"),
1273            Some(b"abc".to_vec())
1274        );
1275        assert_eq!(reader.remaining(), 0);
1276    }
1277
1278    #[test]
1279    fn data_packet_uses_four_byte_length_when_negotiated() {
1280        let packet = encode_packet(
1281            6,
1282            0,
1283            Some(0),
1284            &[0x03, 0x93, 0x01],
1285            PacketLengthWidth::Large32,
1286        )
1287        .expect("packet should encode");
1288        assert_eq!(&packet[..10], &[0, 0, 0, 13, 6, 0, 0, 0, 0, 0]);
1289    }
1290}