use alloc::collections::VecDeque;
use alloc::vec::Vec;
use core::fmt;
use crate::message::{Message, MessageError};
use crate::trace::trace_event;
use crate::v2gtp::{self, PayloadType, V2gtpError};
use crate::{MAX_EXI_PAYLOAD_LEN, Protocol};
pub const MAX_PENDING_FRAMES: usize = 4;
#[derive(Debug)]
pub struct Connection {
protocol: Option<Protocol>,
max_payload_len: usize,
rx: Vec<u8>,
frames: VecDeque<(PayloadType, Vec<u8>)>,
max_pending_frames: usize,
ignored: u32,
tx: VecDeque<u8>,
}
impl Default for Connection {
fn default() -> Self {
Self::new()
}
}
impl Connection {
#[must_use]
pub fn new() -> Self {
Self::with_limit(MAX_EXI_PAYLOAD_LEN)
}
#[must_use]
pub fn with_limit(max_payload_len: usize) -> Self {
Self::with_limits(max_payload_len, MAX_PENDING_FRAMES)
}
#[must_use]
pub fn with_limits(max_payload_len: usize, max_pending_frames: usize) -> Self {
Self {
protocol: None,
max_payload_len,
rx: Vec::new(),
frames: VecDeque::new(),
max_pending_frames: max_pending_frames.max(1),
ignored: 0,
tx: VecDeque::new(),
}
}
#[must_use]
pub const fn protocol(&self) -> Option<Protocol> {
self.protocol
}
pub const fn set_protocol(&mut self, protocol: Protocol) {
self.protocol = Some(protocol);
}
pub fn receive(&mut self, data: &[u8]) -> Result<(), ConnectionError> {
self.rx.extend_from_slice(data);
loop {
match v2gtp::split_frame(&self.rx, self.max_payload_len) {
Ok((header, payload, rest)) => {
if self.frames.len() >= self.max_pending_frames {
self.rx.clear();
self.frames.clear();
return Err(ConnectionError::TooManyFrames {
limit: self.max_pending_frames,
});
}
let consumed = self.rx.len() - rest.len();
self.frames.push_back((header.payload_type, payload.to_vec()));
self.rx.drain(..consumed);
}
Err(V2gtpError::Incomplete) => break,
Err(e) => {
self.rx.clear();
return Err(ConnectionError::Framing(e));
}
}
}
let ceiling = self.max_payload_len.saturating_add(v2gtp::HEADER_LEN);
if self.rx.len() > ceiling {
self.rx.clear();
return Err(ConnectionError::Overflow { limit: ceiling });
}
Ok(())
}
pub fn next_message(&mut self) -> Result<Option<Message>, ConnectionError> {
while let Some((payload_type, payload)) = self.next_frame() {
match Message::decode(self.protocol, payload_type, &payload) {
Ok(message) => return Ok(Some(message)),
Err(MessageError::NotAMessage(_) | MessageError::NotForThisSession { .. }) => {
self.ignored = self.ignored.saturating_add(1);
trace_event!(payload_type = payload_type.as_u16(), "frame ignored");
}
Err(e) => {
self.reset_input();
return Err(e.into());
}
}
}
Ok(None)
}
#[must_use]
pub const fn ignored_frames(&self) -> u32 {
self.ignored
}
#[must_use]
pub fn next_frame(&mut self) -> Option<(PayloadType, Vec<u8>)> {
self.frames.pop_front()
}
pub fn send(&mut self, message: &Message) -> Result<(), ConnectionError> {
let (payload_type, payload) = message.encode()?;
self.send_frame(payload_type, &payload)
}
pub fn send_frame(
&mut self,
payload_type: PayloadType,
payload: &[u8],
) -> Result<(), ConnectionError> {
if payload.len() > self.max_payload_len {
return Err(ConnectionError::Overflow { limit: self.max_payload_len });
}
let mut frame = alloc::vec![0u8; v2gtp::HEADER_LEN + payload.len()];
let n = v2gtp::write_frame(payload_type, payload, &mut frame)
.map_err(ConnectionError::Framing)?;
self.tx.extend(frame[..n].iter().copied());
Ok(())
}
#[must_use]
pub fn transmit_is_empty(&self) -> bool {
self.tx.is_empty()
}
#[must_use]
pub fn take_transmit(&mut self) -> Vec<u8> {
self.tx.drain(..).collect()
}
#[must_use]
pub fn pending_input(&self) -> usize {
self.rx.len()
}
#[must_use]
pub fn pending_frames(&self) -> usize {
self.frames.len()
}
pub fn reset(&mut self) {
self.reset_input();
self.tx.clear();
}
pub fn reset_input(&mut self) {
self.rx.clear();
self.frames.clear();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConnectionError {
Framing(V2gtpError),
Message(MessageError),
Overflow {
limit: usize,
},
TooManyFrames {
limit: usize,
},
}
impl From<V2gtpError> for ConnectionError {
fn from(e: V2gtpError) -> Self {
Self::Framing(e)
}
}
impl From<MessageError> for ConnectionError {
fn from(e: MessageError) -> Self {
Self::Message(e)
}
}
impl fmt::Display for ConnectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Framing(e) => write!(f, "V2GTP: {e}"),
Self::Message(e) => write!(f, "{e}"),
Self::Overflow { limit } => write!(f, "frame exceeds the {limit} byte limit"),
Self::TooManyFrames { limit } => {
write!(f, "the peer pipelined more than {limit} unanswered V2GTP frames")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ConnectionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Framing(e) => Some(e),
Self::Message(e) => Some(e),
Self::Overflow { .. } | Self::TooManyFrames { .. } => None,
}
}
}
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use super::*;
use crate::app_protocol::SupportedAppProtocolReq;
fn handshake() -> Message {
Message::AppProtocolReq(Box::new(SupportedAppProtocolReq::advertising([
Protocol::Iso20,
Protocol::Iso2,
])))
}
#[test]
fn a_frame_split_across_reads_is_reassembled() {
let mut out = Connection::new();
out.send(&handshake()).unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
for chunk in wire.chunks(3) {
c.receive(chunk).unwrap();
}
assert!(c.next_message().unwrap().is_some());
assert_eq!(c.pending_input(), 0);
}
#[test]
fn two_frames_in_one_read_come_out_as_two_messages() {
let mut out = Connection::new();
out.send(&handshake()).unwrap();
out.send(&handshake()).unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.receive(&wire).unwrap();
assert!(c.next_message().unwrap().is_some());
assert!(c.next_message().unwrap().is_some());
assert!(c.next_message().unwrap().is_none());
}
#[test]
fn an_incomplete_frame_is_not_an_error() {
let mut out = Connection::new();
out.send(&handshake()).unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.receive(&wire[..wire.len() - 1]).unwrap();
assert_eq!(c.next_message().unwrap(), None, "still waiting, not broken");
}
#[test]
fn a_forged_length_is_refused_before_anything_is_buffered_for_it() {
let mut c = Connection::with_limit(4096);
assert!(matches!(
c.receive(&[0x01, 0xFE, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]),
Err(ConnectionError::Framing(V2gtpError::PayloadTooLarge { .. }))
));
assert_eq!(c.pending_input(), 0, "nothing is kept from a stream we cannot parse");
}
#[test]
fn a_peer_that_never_finishes_a_frame_cannot_grow_the_buffer() {
const LIMIT: usize = 64;
let ceiling = LIMIT + v2gtp::HEADER_LEN;
let mut c = Connection::with_limit(LIMIT);
c.receive(&[0x01, 0xFE, 0x80, 0x01, 0x00, 0x00, 0x00, u8::try_from(LIMIT).unwrap()])
.unwrap();
for _ in 0..LIMIT - 1 {
c.receive(&[0u8]).unwrap();
assert!(c.pending_input() <= ceiling, "buffered {} bytes", c.pending_input());
}
assert_eq!(c.pending_frames(), 0, "the frame is still one byte short");
}
#[test]
fn garbage_is_rejected_rather_than_skipped() {
let mut c = Connection::new();
assert!(c.receive(&[0xDE, 0xAD, 0xBE, 0xEF, 0, 0, 0, 0]).is_err());
}
#[test]
fn whole_frames_in_one_read_are_not_a_byte_overflow() {
let mut out = Connection::new();
for _ in 0..MAX_PENDING_FRAMES {
out.send(&handshake()).unwrap();
}
let wire = out.take_transmit();
let mut c = Connection::with_limit(wire.len() / MAX_PENDING_FRAMES);
c.receive(&wire).unwrap();
assert_eq!(c.pending_frames(), MAX_PENDING_FRAMES);
assert_eq!(c.pending_input(), 0);
for _ in 0..MAX_PENDING_FRAMES {
assert!(c.next_message().unwrap().is_some());
}
}
#[test]
fn a_peer_that_pipelines_without_waiting_is_cut_off() {
let mut out = Connection::new();
for _ in 0..=MAX_PENDING_FRAMES {
out.send(&handshake()).unwrap();
}
let wire = out.take_transmit();
let mut c = Connection::new();
assert_eq!(
c.receive(&wire),
Err(ConnectionError::TooManyFrames { limit: MAX_PENDING_FRAMES })
);
assert_eq!(c.pending_frames(), 0, "nothing is kept from a stream we will not follow");
assert_eq!(c.pending_input(), 0);
}
#[test]
fn draining_between_reads_has_no_ceiling() {
let mut out = Connection::new();
out.send(&handshake()).unwrap();
let one = out.take_transmit();
let mut c = Connection::with_limits(4096, 1);
for _ in 0..64 {
c.receive(&one).unwrap();
assert!(c.next_message().unwrap().is_some());
}
}
#[test]
fn a_manufacturer_specific_frame_is_skipped_and_the_next_one_still_arrives() {
let mut out = Connection::new();
out.send_frame(PayloadType::ManufacturerSpecific(0xA123), b"vendor").unwrap();
out.send(&handshake()).unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.receive(&wire).unwrap();
assert_eq!(
c.next_message().unwrap().map(|m| m.name()),
Some("supportedAppProtocolReq"),
"the extension is stepped over, not stumbled over"
);
assert_eq!(c.ignored_frames(), 1);
assert!(c.next_message().unwrap().is_none());
}
#[test]
fn a_reserved_payload_type_is_skipped_rather_than_desynchronising_the_stream() {
let mut out = Connection::new();
out.send_frame(PayloadType::Reserved(0x8007), b"?").unwrap();
out.send(&handshake()).unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.receive(&wire).unwrap();
assert!(c.next_message().unwrap().is_some());
assert_eq!(c.ignored_frames(), 1);
}
#[test]
fn a_message_for_another_generation_is_ignored_not_processed() {
let mut out = Connection::new();
out.send_frame(PayloadType::Part20Main, b"not for a -2 session").unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.set_protocol(Protocol::Iso2);
c.receive(&wire).unwrap();
assert_eq!(c.next_message().unwrap(), None, "nothing decoded");
assert_eq!(c.ignored_frames(), 1, "and nothing fatal happened either");
}
#[test]
fn a_missing_codec_for_this_sessions_own_payload_type_is_reported() {
let mut out = Connection::new();
out.send_frame(PayloadType::ExiEncodedV2gMessage, b"a DIN message").unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.set_protocol(Protocol::Din70121);
c.receive(&wire).unwrap();
assert!(matches!(c.next_message(), Err(ConnectionError::Message(_))));
assert_eq!(c.ignored_frames(), 0);
}
#[test]
fn the_negotiated_protocol_decides_how_0x8001_is_read() {
let mut out = Connection::new();
out.send(&handshake()).unwrap();
let wire = out.take_transmit();
let mut c = Connection::new();
c.set_protocol(Protocol::Iso2);
c.receive(&wire).unwrap();
assert!(
c.next_message().is_err(),
"a handshake read as a -2 message must fail, not decode to nonsense"
);
}
}