Skip to main content

noxtls_io/
record.rs

1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18//! TLS record length-prefix framing helpers (transport layer, no cipher semantics).
19
20use crate::internal_alloc::Vec;
21use noxtls_core::{Error, Result};
22
23/// Length of a TLS 1.x `TLSPlaintext` outer header (`type || version || length`).
24pub const TLS_RECORD_HEADER_LEN: usize = 5;
25
26/// Maximum TLS record payload length permitted by RFC 5246 / RFC 8446 outer length field.
27pub const TLS_MAX_RECORD_PAYLOAD_LEN: usize = 1 << 14;
28
29/// Buffers incoming TLS octets and yields complete record packets (`header + payload`).
30///
31/// Supports partial reads from blocking or async transports.
32#[derive(Debug, Default, Clone)]
33pub struct TlsRecordDeframer {
34    buf: Vec<u8>,
35}
36
37impl TlsRecordDeframer {
38    /// Creates an empty deframer buffer.
39    #[must_use]
40    pub fn noxtls_new() -> Self {
41        Self { buf: Vec::new() }
42    }
43
44    /// Appends newly read transport bytes to the internal buffer.
45    pub fn push(&mut self, chunk: &[u8]) {
46        self.buf.extend_from_slice(chunk);
47    }
48
49    /// Returns one complete TLS record packet when `5 + length` bytes are buffered.
50    ///
51    /// # Errors
52    ///
53    /// Returns [`Error::InvalidLength`] when the length field exceeds [`TLS_MAX_RECORD_PAYLOAD_LEN`].
54    pub fn pop_packet(&mut self) -> Result<Option<Vec<u8>>> {
55        if self.buf.len() < TLS_RECORD_HEADER_LEN {
56            return Ok(None);
57        }
58        let payload_len = u16::from_be_bytes([self.buf[3], self.buf[4]]) as usize;
59        if payload_len > TLS_MAX_RECORD_PAYLOAD_LEN {
60            return Err(Error::InvalidLength(
61                "tls record payload exceeds maximum allowed length",
62            ));
63        }
64        let total = TLS_RECORD_HEADER_LEN.saturating_add(payload_len);
65        if self.buf.len() < total {
66            return Ok(None);
67        }
68        let packet = self.buf[..total].to_vec();
69        self.buf.drain(..total);
70        Ok(Some(packet))
71    }
72
73    /// Returns the number of bytes currently buffered.
74    #[must_use]
75    pub fn buffered_len(&self) -> usize {
76        self.buf.len()
77    }
78}