// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::{mem, str}; use neqo_common::{qdebug, qerror}; use neqo_transport::{Connection, StreamId}; use crate::{Error, Res, huffman, prefix::Prefix}; pub trait ReadByte { /// # Errors /// /// Return error occurred while reading a byte. /// The exact error depends on trait implementation. fn read_byte(&mut self) -> Res; } pub trait Reader { /// # Errors /// /// Return error occurred while reading date into a buffer. /// The exact error depends on trait implementation. fn read(&mut self, buf: &mut [u8]) -> Res; } pub(crate) struct ReceiverConnWrapper<'a> { conn: &'a mut Connection, stream_id: StreamId, } impl ReadByte for ReceiverConnWrapper<'_> { fn read_byte(&mut self) -> Res { let mut b = [0]; match self.conn.stream_recv(self.stream_id, &mut b)? { (_, true) => Err(Error::ClosedCriticalStream), (0, false) => Err(Error::NeedMoreData), _ => Ok(b[0]), } } } impl Reader for ReceiverConnWrapper<'_> { fn read(&mut self, buf: &mut [u8]) -> Res { match self.conn.stream_recv(self.stream_id, buf)? { (_, true) => Err(Error::ClosedCriticalStream), (amount, false) => Ok(amount), } } } impl<'a> ReceiverConnWrapper<'a> { pub const fn new(conn: &'a mut Connection, stream_id: StreamId) -> Self { Self { conn, stream_id } } } /// This is only used by header decoder therefore all errors are `Error::Decompression`. /// A header block is read entirely before decoding it, therefore if there is not enough /// data in the buffer an error `Error::Decompression` will be return. pub(crate) struct ReceiverBufferWrapper<'a> { buf: &'a [u8], offset: usize, } impl ReadByte for ReceiverBufferWrapper<'_> { fn read_byte(&mut self) -> Res { if self.offset == self.buf.len() { Err(Error::Decompression) } else { let b = self.buf[self.offset]; self.offset += 1; Ok(b) } } } impl<'a> ReceiverBufferWrapper<'a> { pub const fn new(buf: &'a [u8]) -> Self { Self { buf, offset: 0 } } pub const fn peek(&self) -> Res { if self.offset == self.buf.len() { Err(Error::Decompression) } else { Ok(self.buf[self.offset]) } } pub const fn done(&self) -> bool { self.offset == self.buf.len() } /// The function decodes varint with a prefixed, i.e. ignores `prefix_len` bits of the first /// byte. /// `ReceiverBufferWrapper` is only used for decoding header blocks. The header blocks are read /// entirely before a decoding starts, therefore any incomplete varint because of reaching the /// end of a buffer will be treated as the `Error::Decompression` error. pub fn read_prefixed_int(&mut self, prefix_len: u8) -> Res { debug_assert!(prefix_len < 8); let first_byte = self.read_byte()?; let mut reader = IntReader::new(first_byte, prefix_len); reader.read(self) } /// Do not use `LiteralReader` here to avoid copying data. /// The function decoded a literal with a prefix: /// 1) ignores `prefix_len` bits of the first byte, /// 2) reads "huffman bit" /// 3) decode varint that is the length of a literal /// 4) reads the literal /// 5) performs huffman decoding if needed. /// /// `ReceiverBufferWrapper` is only used for decoding header blocks. The header blocks are read /// entirely before a decoding starts, therefore any incomplete varint or literal because of /// reaching the end of a buffer will be treated as the `Error::Decompression` error. pub fn read_literal_from_buffer(&mut self, prefix_len: u8) -> Res> { debug_assert!(prefix_len < 7); let first_byte = self.read_byte()?; let use_huffman = (first_byte & (0x80 >> prefix_len)) != 0; let mut int_reader = IntReader::new(first_byte, prefix_len + 1); let length: usize = int_reader .read(self)? .try_into() .ok() .filter(|&l| l <= LiteralReader::MAX_LEN) .ok_or(Error::Decompression)?; if use_huffman { huffman::decode(self.slice(length)?) } else { Ok(self.slice(length)?.to_vec()) } } fn slice(&mut self, len: usize) -> Res<&[u8]> { let end = self.offset.checked_add(len).ok_or(Error::Decompression)?; if end > self.buf.len() { Err(Error::Decompression) } else { let start = self.offset; self.offset = end; Ok(&self.buf[start..self.offset]) } } } /// This is varint reader that can take into account a prefix. #[derive(Debug)] #[expect(clippy::module_name_repetitions, reason = "This is OK.")] pub struct IntReader { value: u64, cnt: u8, done: bool, } impl IntReader { /// `IntReader` is created by supplying the first byte and prefix length. /// A varint may take only one byte, In that case already the first by has set state to done. /// /// # Panics /// /// When `prefix_len` is 8 or larger. #[must_use] pub fn new(first_byte: u8, prefix_len: u8) -> Self { debug_assert!(prefix_len < 8, "prefix cannot larger than 7"); let mask = if prefix_len == 0 { 0xff } else { (1 << (8 - prefix_len)) - 1 }; let value = u64::from(first_byte & mask); Self { value, cnt: 0, done: value < u64::from(mask), } } /// # Panics /// /// Never, but rust doesn't know that. #[must_use] pub fn make(first_byte: u8, prefixes: &[Prefix]) -> Self { for prefix in prefixes { if prefix.cmp_prefix(first_byte) { return Self::new(first_byte, prefix.len()); } } unreachable!(); } /// This function reads bytes until the varint is decoded or until stream/buffer does not /// have any more date. /// /// # Errors /// /// Possible errors are: /// 1) `NeedMoreData` if the reader needs more data, /// 2) `IntegerOverflow`, /// 3) Any `ReadByte`'s error pub fn read(&mut self, s: &mut R) -> Res { let mut b: u8; while !self.done { b = s.read_byte()?; if (self.cnt == 63) && (b > 1 || (b == 1 && ((self.value >> 63) == 1))) { qerror!("Error decoding prefixed encoded int - IntegerOverflow"); return Err(Error::IntegerOverflow); } self.value += u64::from(b & 0x7f) << self.cnt; if (b & 0x80) == 0 { self.done = true; } self.cnt += 7; if self.cnt >= 64 { self.done = true; } } Ok(self.value) } } #[derive(Debug, Default)] enum LiteralReaderState { #[default] ReadHuffman, ReadLength { reader: IntReader, }, ReadLiteral { offset: usize, }, Done, } /// This is decoder of a literal with a prefix: /// 1) ignores `prefix_len` bits of the first byte, /// 2) reads "huffman bit" /// 3) decode varint that is the length of a literal /// 4) reads the literal /// 5) performs huffman decoding if needed. #[derive(Debug, Default)] #[expect(clippy::module_name_repetitions, reason = "This is OK.")] pub struct LiteralReader { state: LiteralReaderState, literal: Vec, use_huffman: bool, } impl LiteralReader { /// Maximum length for a literal string in QPACK encoding. /// /// RFC 9204 requires implementations to set their own limits for string literal /// lengths to prevent denial-of-service attacks. The RFC does not mandate a /// specific value, stating only that limits "SHOULD be large enough to process /// the largest individual field the HTTP implementation can be configured to /// accept." /// /// The Gecko limit is in `network.http.max_response_header_size` and defaults to /// 393216 bytes (384 KB), see `modules/libpref/init/StaticPrefList.yaml`. We use /// the same limit. const MAX_LEN: usize = 384 * 1024; /// Creates `LiteralReader` with the first byte. This constructor is always used /// when a literal has a prefix. /// For literals without a prefix please use the default constructor. /// /// # Panics /// /// If `prefix_len` is 8 or more. #[must_use] pub fn new_with_first_byte(first_byte: u8, prefix_len: u8) -> Self { assert!(prefix_len < 8); Self { state: LiteralReaderState::ReadLength { reader: IntReader::new(first_byte, prefix_len + 1), }, literal: Vec::new(), use_huffman: (first_byte & (0x80 >> prefix_len)) != 0, } } /// This function reads bytes until the literal is decoded or until stream/buffer does not /// have any more date ready. /// /// # Errors /// /// Possible errors are: /// 1) `NeedMoreData` if the reader needs more data, /// 2) `IntegerOverflow` /// 3) Any `ReadByte`'s error /// /// It returns value if reading the literal is done or None if it needs more data. /// /// # Panics /// /// When this object is complete. pub fn read(&mut self, s: &mut T) -> Res> { loop { qdebug!("state = {:?}", self.state); match &mut self.state { LiteralReaderState::ReadHuffman => { let b = s.read_byte()?; self.use_huffman = (b & 0x80) != 0; self.state = LiteralReaderState::ReadLength { reader: IntReader::new(b, 1), }; } LiteralReaderState::ReadLength { reader } => { let v = usize::try_from(reader.read(s)?) .ok() .filter(|&l| l <= Self::MAX_LEN) .ok_or(Error::Decoding)?; self.literal.resize(v, 0x0); self.state = LiteralReaderState::ReadLiteral { offset: 0 }; } LiteralReaderState::ReadLiteral { offset } => { let amount = s.read(&mut self.literal[*offset..])?; *offset += amount; if *offset == self.literal.len() { self.state = LiteralReaderState::Done; if self.use_huffman { break Ok(huffman::decode(&self.literal)?); } break Ok(mem::take(&mut self.literal)); } break Err(Error::NeedMoreData); } LiteralReaderState::Done => { panic!("Should not call read() in this state"); } } } } } /// This is a helper function used only by `ReceiverBufferWrapper`, therefore it returns /// `Error::Decompression` if any error happens. /// /// # Errors /// /// If an parsing error occurred, the function returns `BadUtf8`. pub fn parse_utf8(v: &[u8]) -> Res<&str> { str::from_utf8(v).map_err(|_| Error::BadUtf8) } #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] pub(crate) mod test_receiver { use std::collections::VecDeque; use super::{Error, ReadByte, Reader, Res}; #[derive(Default)] pub struct TestReceiver { buf: VecDeque, } impl ReadByte for TestReceiver { fn read_byte(&mut self) -> Res { self.buf.pop_back().ok_or(Error::NeedMoreData) } } impl Reader for TestReceiver { fn read(&mut self, buf: &mut [u8]) -> Res { let len = if buf.len() > self.buf.len() { self.buf.len() } else { buf.len() }; for item in buf.iter_mut().take(len) { *item = self.buf.pop_back().ok_or(Error::NeedMoreData)?; } Ok(len) } } impl TestReceiver { pub fn write(&mut self, buf: &[u8]) { for b in buf { self.buf.push_front(*b); } } } } #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] mod tests { use neqo_common::Encoder; use test_receiver::TestReceiver; use super::{ Error, IntReader, LiteralReader, ReadByte as _, ReceiverBufferWrapper, Res, huffman, test_receiver, }; use crate::{prefix::Prefix, qpack_send_buf::Encoder as _}; const TEST_CASES_NUMBERS: [(&[u8], u8, u64); 7] = [ (&[0xEA], 3, 10), (&[0x0A], 3, 10), (&[0x8A], 3, 10), (&[0xFF, 0x9A, 0x0A], 3, 1337), (&[0x1F, 0x9A, 0x0A], 3, 1337), (&[0x9F, 0x9A, 0x0A], 3, 1337), (&[0x2A], 0, 42), ]; #[test] fn read_prefixed_int() { for (buf, prefix_len, value) in &TEST_CASES_NUMBERS { let mut reader = IntReader::new(buf[0], *prefix_len); let mut test_receiver: TestReceiver = TestReceiver::default(); test_receiver.write(&buf[1..]); assert_eq!(reader.read(&mut test_receiver), Ok(*value)); } } #[test] fn read_prefixed_int_with_more_data_in_buffer() { for (buf, prefix_len, value) in &TEST_CASES_NUMBERS { let mut reader = IntReader::new(buf[0], *prefix_len); let mut test_receiver: TestReceiver = TestReceiver::default(); test_receiver.write(&buf[1..]); // add some more data test_receiver.write(&[0x0, 0x0, 0x0]); assert_eq!(reader.read(&mut test_receiver), Ok(*value)); } } #[test] fn read_prefixed_int_slow_writer() { let (buf, prefix_len, value) = &TEST_CASES_NUMBERS[4]; assert!(buf.len() > 1); let mut reader = IntReader::new(buf[0], *prefix_len); let mut test_receiver: TestReceiver = TestReceiver::default(); // data has not been received yet, reading IntReader will return Err(Error::NeedMoreData). assert_eq!(reader.read(&mut test_receiver), Err(Error::NeedMoreData)); // Write one byte. test_receiver.write(&buf[1..2]); // data has not been received yet, reading IntReader will return Err(Error::NeedMoreData). assert_eq!(reader.read(&mut test_receiver), Err(Error::NeedMoreData)); // Write one byte. test_receiver.write(&buf[2..]); // Now prefixed int is complete. assert_eq!(reader.read(&mut test_receiver), Ok(*value)); } type TestSetup = (&'static [u8], u8, Res); const TEST_CASES_BIG_NUMBERS: [TestSetup; 3] = [ ( &[ 0xFF, 0x80, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, ], 0, Ok(0xFFFF_FFFF_FFFF_FFFF), ), ( &[ 0xFF, 0x81, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, ], 0, Err(Error::IntegerOverflow), ), ( &[ 0xFF, 0x80, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, ], 0, Err(Error::IntegerOverflow), ), ]; #[test] fn read_prefixed_int_big_number() { for (buf, prefix_len, value) in &TEST_CASES_BIG_NUMBERS { let mut reader = IntReader::new(buf[0], *prefix_len); let mut test_receiver: TestReceiver = TestReceiver::default(); test_receiver.write(&buf[1..]); assert_eq!(reader.read(&mut test_receiver), *value); } } const TEST_CASES_LITERAL: [(&[u8], u8, &[u8]); 9] = [ // No Huffman ( &[ 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, ], 1, b"custom-key", ), ( &[ 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, ], 3, b"custom-key", ), ( &[ 0xea, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, ], 3, b"custom-key", ), ( &[ 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, ], 1, b"custom-header", ), // With Huffman (&[0x15, 0xae, 0xc3, 0x77, 0x1a, 0x4b], 3, b"private"), ( &[ 0x56, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b, 0xff, ], 1, b"Mon, 21 Oct 2013 20:13:21 GMT", ), ( &[ 0xff, 0x0f, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b, 0xff, ], 4, b"Mon, 21 Oct 2013 20:13:21 GMT", ), ( &[ 0x51, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3, ], 1, b"https://www.example.com", ), ( &[ 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3, ], 0, b"https://www.example.com", ), ]; #[test] fn read_literal() { for (buf, prefix_len, value) in &TEST_CASES_LITERAL { let mut reader = LiteralReader::new_with_first_byte(buf[0], *prefix_len); let mut test_receiver: TestReceiver = TestReceiver::default(); test_receiver.write(&buf[1..]); assert_eq!(reader.read(&mut test_receiver).unwrap().as_slice(), *value); } } #[test] fn read_prefixed_int_receiver_buffer_wrapper() { for (buf, prefix_len, value) in &TEST_CASES_NUMBERS { let mut buffer = ReceiverBufferWrapper::new(buf); let mut reader = IntReader::new(buffer.read_byte().unwrap(), *prefix_len); assert_eq!(reader.read(&mut buffer), Ok(*value)); } } #[test] fn read_prefixed_int_big_receiver_buffer_wrapper() { for (buf, prefix_len, value) in &TEST_CASES_BIG_NUMBERS { let mut buffer = ReceiverBufferWrapper::new(buf); let mut reader = IntReader::new(buffer.read_byte().unwrap(), *prefix_len); assert_eq!(reader.read(&mut buffer), *value); } } #[test] fn read_literal_receiver_buffer_wrapper() { for (buf, prefix_len, value) in &TEST_CASES_LITERAL { let mut buffer = ReceiverBufferWrapper::new(buf); assert_eq!( buffer.read_literal_from_buffer(*prefix_len).unwrap(), *value ); } } #[test] fn read_failure_receiver_buffer_wrapper_number() { let (buf, prefix_len, _) = &TEST_CASES_NUMBERS[4]; let mut buffer = ReceiverBufferWrapper::new(&buf[..1]); let mut reader = IntReader::new(buffer.read_byte().unwrap(), *prefix_len); assert_eq!(reader.read(&mut buffer), Err(Error::Decompression)); } #[test] fn read_failure_receiver_buffer_wrapper_literal() { let (buf, prefix_len, _) = &TEST_CASES_LITERAL[0]; let mut buffer = ReceiverBufferWrapper::new(&buf[..6]); assert_eq!( buffer.read_literal_from_buffer(*prefix_len), Err(Error::Decompression) ); } #[test] fn read_non_utf8_huffman_literal() { // Test non-UTF8 data with Huffman encoding // 0xE4 is 'รค' in ISO-8859-1 (extended ASCII), which is invalid UTF-8 let non_utf8_data = &[0xE4u8]; let encoded = huffman::encode(non_utf8_data); // Build a QPACK literal: [huffman_bit | length][data] // For prefix_len=3, the huffman bit is at position (0x80 >> 3) = 0x10 let mut buf = Vec::new(); #[expect(clippy::cast_possible_truncation, reason = "Test data is small")] let len = encoded.len() as u8; buf.push(0x10 | len); // Huffman bit set + length buf.extend_from_slice(&encoded); let mut buffer = ReceiverBufferWrapper::new(&buf); let result = buffer.read_literal_from_buffer(3).unwrap(); assert_eq!(result, non_utf8_data); } #[test] fn read_non_utf8_plain_literal() { // Test non-UTF8 data without Huffman encoding // 0xFF, 0xFE are invalid UTF-8 sequences let non_utf8_data = &[0xFFu8, 0xFEu8]; // Build a QPACK literal without Huffman: [length][data] // For prefix_len=3, no huffman bit let mut buf = Vec::new(); #[expect(clippy::cast_possible_truncation, reason = "Test data is small")] let len = non_utf8_data.len() as u8; buf.push(len); // No Huffman bit, just length buf.extend_from_slice(non_utf8_data); let mut buffer = ReceiverBufferWrapper::new(&buf); let result = buffer.read_literal_from_buffer(3).unwrap(); assert_eq!(result, non_utf8_data); } /// Create a [`LiteralReader`] and [`TestReceiver`] for a literal with the given length. fn literal_reader_for_test(literal_len: usize) -> (LiteralReader, TestReceiver) { const PREFIX_LEN: u8 = 3; let mut data = Encoder::default(); data.encode_literal( false, Prefix::new(0x00, PREFIX_LEN), &vec![b'a'; literal_len], ); let reader = LiteralReader::new_with_first_byte(data.as_ref()[0], PREFIX_LEN); let mut test_receiver = TestReceiver::default(); test_receiver.write(&data.as_ref()[1..]); (reader, test_receiver) } /// Test that [`LiteralReader`] rejects literals exceeding [`MAX_LEN`]. /// /// This prevents denial-of-service attacks where a malicious QPACK encoder /// sends an extremely large length value to trigger excessive memory allocation. /// RFC 9204 requires implementations to set their own limits for string literal /// lengths. #[test] fn literal_exceeding_max_len_rejected() { let (mut reader, mut test_receiver) = literal_reader_for_test(LiteralReader::MAX_LEN + 1); assert_eq!(reader.read(&mut test_receiver), Err(Error::Decoding)); } /// Test that [`LiteralReader`] accepts literals at exactly [`MAX_LEN`]. #[test] fn literal_at_max_len_accepted() { let (mut reader, mut test_receiver) = literal_reader_for_test(LiteralReader::MAX_LEN); let result = reader.read(&mut test_receiver).unwrap(); assert_eq!(result.len(), LiteralReader::MAX_LEN); } #[test] fn buffer_wrapper_rejects_oversized_literal() { const PREFIX_LEN: u8 = 3; // Encode only the length field (MAX_LEN + 1) without allocating the actual data. // The validation should fail before attempting to read the literal content. let mut data = Encoder::default(); data.encode_prefixed_encoded_int( Prefix::new(0x00, PREFIX_LEN + 1), (LiteralReader::MAX_LEN + 1) as u64, ); let mut buffer = ReceiverBufferWrapper::new(data.as_ref()); assert_eq!( buffer.read_literal_from_buffer(PREFIX_LEN), Err(Error::Decompression) ); } #[test] fn buffer_wrapper_slice_detects_overflow() { let buf = [0u8; 10]; let mut wrapper = ReceiverBufferWrapper::new(&buf); wrapper.offset = 5; assert_eq!(wrapper.slice(7), Err(Error::Decompression)); assert_eq!(wrapper.slice(usize::MAX), Err(Error::Decompression)); } }