| // Copyright (C) 2018-2019, Cloudflare, Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| use std::convert::TryInto; |
| |
| use crate::Error; |
| use crate::Result; |
| |
| use crate::packet; |
| use crate::ranges; |
| use crate::stream; |
| |
| #[cfg(feature = "qlog")] |
| use qlog::events::quic::AckedRanges; |
| #[cfg(feature = "qlog")] |
| use qlog::events::quic::ErrorSpace; |
| #[cfg(feature = "qlog")] |
| use qlog::events::quic::QuicFrame; |
| #[cfg(feature = "qlog")] |
| use qlog::events::quic::StreamType; |
| |
| pub const MAX_CRYPTO_OVERHEAD: usize = 8; |
| pub const MAX_DGRAM_OVERHEAD: usize = 2; |
| pub const MAX_STREAM_OVERHEAD: usize = 12; |
| pub const MAX_STREAM_SIZE: u64 = 1 << 62; |
| |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub struct EcnCounts { |
| ect0_count: u64, |
| ect1_count: u64, |
| ecn_ce_count: u64, |
| } |
| |
| #[derive(Clone, PartialEq, Eq)] |
| pub enum Frame { |
| Padding { |
| len: usize, |
| }, |
| |
| Ping { |
| // Attach metadata to the Ping frame. This doesn't appear on the wire, |
| // but does tell us if this frame was part of a PMTUD probe and how |
| // large the probe was. This will only show up on sent frames and be |
| // None otherwise. This is the total size of the QUIC packet in the |
| // probe. |
| mtu_probe: Option<usize>, |
| }, |
| |
| ACK { |
| ack_delay: u64, |
| ranges: ranges::RangeSet, |
| ecn_counts: Option<EcnCounts>, |
| }, |
| |
| ResetStream { |
| stream_id: u64, |
| error_code: u64, |
| final_size: u64, |
| }, |
| |
| StopSending { |
| stream_id: u64, |
| error_code: u64, |
| }, |
| |
| Crypto { |
| data: stream::RangeBuf, |
| }, |
| |
| CryptoHeader { |
| offset: u64, |
| length: usize, |
| }, |
| |
| NewToken { |
| token: Vec<u8>, |
| }, |
| |
| Stream { |
| stream_id: u64, |
| data: stream::RangeBuf, |
| }, |
| |
| StreamHeader { |
| stream_id: u64, |
| offset: u64, |
| length: usize, |
| fin: bool, |
| }, |
| |
| MaxData { |
| max: u64, |
| }, |
| |
| MaxStreamData { |
| stream_id: u64, |
| max: u64, |
| }, |
| |
| MaxStreamsBidi { |
| max: u64, |
| }, |
| |
| MaxStreamsUni { |
| max: u64, |
| }, |
| |
| DataBlocked { |
| limit: u64, |
| }, |
| |
| StreamDataBlocked { |
| stream_id: u64, |
| limit: u64, |
| }, |
| |
| StreamsBlockedBidi { |
| limit: u64, |
| }, |
| |
| StreamsBlockedUni { |
| limit: u64, |
| }, |
| |
| NewConnectionId { |
| seq_num: u64, |
| retire_prior_to: u64, |
| conn_id: Vec<u8>, |
| reset_token: [u8; 16], |
| }, |
| |
| RetireConnectionId { |
| seq_num: u64, |
| }, |
| |
| PathChallenge { |
| data: [u8; 8], |
| }, |
| |
| PathResponse { |
| data: [u8; 8], |
| }, |
| |
| ConnectionClose { |
| error_code: u64, |
| frame_type: u64, |
| reason: Vec<u8>, |
| }, |
| |
| ApplicationClose { |
| error_code: u64, |
| reason: Vec<u8>, |
| }, |
| |
| HandshakeDone, |
| |
| Datagram { |
| data: Vec<u8>, |
| }, |
| |
| DatagramHeader { |
| length: usize, |
| }, |
| } |
| |
| impl Frame { |
| pub fn from_bytes( |
| b: &mut octets::Octets, pkt: packet::Type, |
| ) -> Result<Frame> { |
| let frame_type = b.get_varint()?; |
| |
| let frame = match frame_type { |
| 0x00 => { |
| let mut len = 1; |
| |
| while b.peek_u8() == Ok(0x00) { |
| b.get_u8()?; |
| |
| len += 1; |
| } |
| |
| Frame::Padding { len } |
| }, |
| |
| 0x01 => Frame::Ping { mtu_probe: None }, |
| |
| 0x02..=0x03 => parse_ack_frame(frame_type, b)?, |
| |
| 0x04 => Frame::ResetStream { |
| stream_id: b.get_varint()?, |
| error_code: b.get_varint()?, |
| final_size: b.get_varint()?, |
| }, |
| |
| 0x05 => Frame::StopSending { |
| stream_id: b.get_varint()?, |
| error_code: b.get_varint()?, |
| }, |
| |
| 0x06 => { |
| let offset = b.get_varint()?; |
| let data = b.get_bytes_with_varint_length()?; |
| let data = stream::RangeBuf::from(data.as_ref(), offset, false); |
| |
| Frame::Crypto { data } |
| }, |
| |
| 0x07 => { |
| let len = b.get_varint()?; |
| if len == 0 { |
| return Err(Error::InvalidFrame); |
| } |
| |
| Frame::NewToken { |
| token: b.get_bytes(len as usize)?.to_vec(), |
| } |
| }, |
| |
| 0x08..=0x0f => parse_stream_frame(frame_type, b)?, |
| |
| 0x10 => Frame::MaxData { |
| max: b.get_varint()?, |
| }, |
| |
| 0x11 => Frame::MaxStreamData { |
| stream_id: b.get_varint()?, |
| max: b.get_varint()?, |
| }, |
| |
| 0x12 => Frame::MaxStreamsBidi { |
| max: b.get_varint()?, |
| }, |
| |
| 0x13 => Frame::MaxStreamsUni { |
| max: b.get_varint()?, |
| }, |
| |
| 0x14 => Frame::DataBlocked { |
| limit: b.get_varint()?, |
| }, |
| |
| 0x15 => Frame::StreamDataBlocked { |
| stream_id: b.get_varint()?, |
| limit: b.get_varint()?, |
| }, |
| |
| 0x16 => Frame::StreamsBlockedBidi { |
| limit: b.get_varint()?, |
| }, |
| |
| 0x17 => Frame::StreamsBlockedUni { |
| limit: b.get_varint()?, |
| }, |
| |
| 0x18 => { |
| let seq_num = b.get_varint()?; |
| let retire_prior_to = b.get_varint()?; |
| let conn_id_len = b.get_u8()?; |
| |
| if !(1..=packet::MAX_CID_LEN).contains(&conn_id_len) { |
| return Err(Error::InvalidFrame); |
| } |
| |
| Frame::NewConnectionId { |
| seq_num, |
| retire_prior_to, |
| conn_id: b.get_bytes(conn_id_len as usize)?.to_vec(), |
| reset_token: b |
| .get_bytes(16)? |
| .buf() |
| .try_into() |
| .map_err(|_| Error::BufferTooShort)?, |
| } |
| }, |
| |
| 0x19 => Frame::RetireConnectionId { |
| seq_num: b.get_varint()?, |
| }, |
| |
| 0x1a => Frame::PathChallenge { |
| data: b |
| .get_bytes(8)? |
| .buf() |
| .try_into() |
| .map_err(|_| Error::BufferTooShort)?, |
| }, |
| |
| 0x1b => Frame::PathResponse { |
| data: b |
| .get_bytes(8)? |
| .buf() |
| .try_into() |
| .map_err(|_| Error::BufferTooShort)?, |
| }, |
| |
| 0x1c => Frame::ConnectionClose { |
| error_code: b.get_varint()?, |
| frame_type: b.get_varint()?, |
| reason: b.get_bytes_with_varint_length()?.to_vec(), |
| }, |
| |
| 0x1d => Frame::ApplicationClose { |
| error_code: b.get_varint()?, |
| reason: b.get_bytes_with_varint_length()?.to_vec(), |
| }, |
| |
| 0x1e => Frame::HandshakeDone, |
| |
| 0x30 | 0x31 => parse_datagram_frame(frame_type, b)?, |
| |
| _ => return Err(Error::InvalidFrame), |
| }; |
| |
| let allowed = match (pkt, &frame) { |
| // PADDING and PING are allowed on all packet types. |
| (_, Frame::Padding { .. }) | (_, Frame::Ping { .. }) => true, |
| |
| // ACK, CRYPTO, HANDSHAKE_DONE, NEW_TOKEN, PATH_RESPONSE, and |
| // RETIRE_CONNECTION_ID can't be sent on 0-RTT packets. |
| (packet::Type::ZeroRTT, Frame::ACK { .. }) => false, |
| (packet::Type::ZeroRTT, Frame::Crypto { .. }) => false, |
| (packet::Type::ZeroRTT, Frame::HandshakeDone) => false, |
| (packet::Type::ZeroRTT, Frame::NewToken { .. }) => false, |
| (packet::Type::ZeroRTT, Frame::PathResponse { .. }) => false, |
| (packet::Type::ZeroRTT, Frame::RetireConnectionId { .. }) => false, |
| (packet::Type::ZeroRTT, Frame::ConnectionClose { .. }) => false, |
| |
| // ACK, CRYPTO and CONNECTION_CLOSE can be sent on all other packet |
| // types. |
| (_, Frame::ACK { .. }) => true, |
| (_, Frame::Crypto { .. }) => true, |
| (_, Frame::ConnectionClose { .. }) => true, |
| |
| // All frames are allowed on 0-RTT and 1-RTT packets. |
| (packet::Type::Short, _) => true, |
| (packet::Type::ZeroRTT, _) => true, |
| |
| // All other cases are forbidden. |
| (..) => false, |
| }; |
| |
| if !allowed { |
| return Err(Error::InvalidPacket); |
| } |
| |
| Ok(frame) |
| } |
| |
| pub fn to_bytes(&self, b: &mut octets::OctetsMut) -> Result<usize> { |
| let before = b.cap(); |
| |
| match self { |
| Frame::Padding { len } => { |
| let mut left = *len; |
| |
| while left > 0 { |
| b.put_varint(0x00)?; |
| |
| left -= 1; |
| } |
| }, |
| |
| Frame::Ping { .. } => { |
| b.put_varint(0x01)?; |
| }, |
| |
| Frame::ACK { |
| ack_delay, |
| ranges, |
| ecn_counts, |
| } => { |
| if ecn_counts.is_none() { |
| b.put_varint(0x02)?; |
| } else { |
| b.put_varint(0x03)?; |
| } |
| |
| let mut it = ranges.iter().rev(); |
| |
| let first = it.next().unwrap(); |
| let ack_block = (first.end - 1) - first.start; |
| |
| b.put_varint(first.end - 1)?; |
| b.put_varint(*ack_delay)?; |
| b.put_varint(it.len() as u64)?; |
| b.put_varint(ack_block)?; |
| |
| let mut smallest_ack = first.start; |
| |
| for block in it { |
| let gap = smallest_ack - block.end - 1; |
| let ack_block = (block.end - 1) - block.start; |
| |
| b.put_varint(gap)?; |
| b.put_varint(ack_block)?; |
| |
| smallest_ack = block.start; |
| } |
| |
| if let Some(ecn) = ecn_counts { |
| b.put_varint(ecn.ect0_count)?; |
| b.put_varint(ecn.ect1_count)?; |
| b.put_varint(ecn.ecn_ce_count)?; |
| } |
| }, |
| |
| Frame::ResetStream { |
| stream_id, |
| error_code, |
| final_size, |
| } => { |
| b.put_varint(0x04)?; |
| |
| b.put_varint(*stream_id)?; |
| b.put_varint(*error_code)?; |
| b.put_varint(*final_size)?; |
| }, |
| |
| Frame::StopSending { |
| stream_id, |
| error_code, |
| } => { |
| b.put_varint(0x05)?; |
| |
| b.put_varint(*stream_id)?; |
| b.put_varint(*error_code)?; |
| }, |
| |
| Frame::Crypto { data } => { |
| encode_crypto_header(data.off(), data.len() as u64, b)?; |
| |
| b.put_bytes(data)?; |
| }, |
| |
| Frame::CryptoHeader { .. } => (), |
| |
| Frame::NewToken { token } => { |
| b.put_varint(0x07)?; |
| |
| b.put_varint(token.len() as u64)?; |
| b.put_bytes(token)?; |
| }, |
| |
| Frame::Stream { stream_id, data } => { |
| encode_stream_header( |
| *stream_id, |
| data.off(), |
| data.len() as u64, |
| data.fin(), |
| b, |
| )?; |
| |
| b.put_bytes(data)?; |
| }, |
| |
| Frame::StreamHeader { .. } => (), |
| |
| Frame::MaxData { max } => { |
| b.put_varint(0x10)?; |
| |
| b.put_varint(*max)?; |
| }, |
| |
| Frame::MaxStreamData { stream_id, max } => { |
| b.put_varint(0x11)?; |
| |
| b.put_varint(*stream_id)?; |
| b.put_varint(*max)?; |
| }, |
| |
| Frame::MaxStreamsBidi { max } => { |
| b.put_varint(0x12)?; |
| |
| b.put_varint(*max)?; |
| }, |
| |
| Frame::MaxStreamsUni { max } => { |
| b.put_varint(0x13)?; |
| |
| b.put_varint(*max)?; |
| }, |
| |
| Frame::DataBlocked { limit } => { |
| b.put_varint(0x14)?; |
| |
| b.put_varint(*limit)?; |
| }, |
| |
| Frame::StreamDataBlocked { stream_id, limit } => { |
| b.put_varint(0x15)?; |
| |
| b.put_varint(*stream_id)?; |
| b.put_varint(*limit)?; |
| }, |
| |
| Frame::StreamsBlockedBidi { limit } => { |
| b.put_varint(0x16)?; |
| |
| b.put_varint(*limit)?; |
| }, |
| |
| Frame::StreamsBlockedUni { limit } => { |
| b.put_varint(0x17)?; |
| |
| b.put_varint(*limit)?; |
| }, |
| |
| Frame::NewConnectionId { |
| seq_num, |
| retire_prior_to, |
| conn_id, |
| reset_token, |
| } => { |
| b.put_varint(0x18)?; |
| |
| b.put_varint(*seq_num)?; |
| b.put_varint(*retire_prior_to)?; |
| b.put_u8(conn_id.len() as u8)?; |
| b.put_bytes(conn_id.as_ref())?; |
| b.put_bytes(reset_token.as_ref())?; |
| }, |
| |
| Frame::RetireConnectionId { seq_num } => { |
| b.put_varint(0x19)?; |
| |
| b.put_varint(*seq_num)?; |
| }, |
| |
| Frame::PathChallenge { data } => { |
| b.put_varint(0x1a)?; |
| |
| b.put_bytes(data.as_ref())?; |
| }, |
| |
| Frame::PathResponse { data } => { |
| b.put_varint(0x1b)?; |
| |
| b.put_bytes(data.as_ref())?; |
| }, |
| |
| Frame::ConnectionClose { |
| error_code, |
| frame_type, |
| reason, |
| } => { |
| b.put_varint(0x1c)?; |
| |
| b.put_varint(*error_code)?; |
| b.put_varint(*frame_type)?; |
| b.put_varint(reason.len() as u64)?; |
| b.put_bytes(reason.as_ref())?; |
| }, |
| |
| Frame::ApplicationClose { error_code, reason } => { |
| b.put_varint(0x1d)?; |
| |
| b.put_varint(*error_code)?; |
| b.put_varint(reason.len() as u64)?; |
| b.put_bytes(reason.as_ref())?; |
| }, |
| |
| Frame::HandshakeDone => { |
| b.put_varint(0x1e)?; |
| }, |
| |
| Frame::Datagram { data } => { |
| encode_dgram_header(data.len() as u64, b)?; |
| |
| b.put_bytes(data.as_ref())?; |
| }, |
| |
| Frame::DatagramHeader { .. } => (), |
| } |
| |
| Ok(before - b.cap()) |
| } |
| |
| pub fn wire_len(&self) -> usize { |
| match self { |
| Frame::Padding { len } => *len, |
| |
| Frame::Ping { .. } => 1, |
| |
| Frame::ACK { |
| ack_delay, |
| ranges, |
| ecn_counts, |
| } => { |
| let mut it = ranges.iter().rev(); |
| |
| let first = it.next().unwrap(); |
| let ack_block = (first.end - 1) - first.start; |
| |
| let mut len = 1 + // frame type |
| octets::varint_len(first.end - 1) + // largest_ack |
| octets::varint_len(*ack_delay) + // ack_delay |
| octets::varint_len(it.len() as u64) + // block_count |
| octets::varint_len(ack_block); // first_block |
| |
| let mut smallest_ack = first.start; |
| |
| for block in it { |
| let gap = smallest_ack - block.end - 1; |
| let ack_block = (block.end - 1) - block.start; |
| |
| len += octets::varint_len(gap) + // gap |
| octets::varint_len(ack_block); // ack_block |
| |
| smallest_ack = block.start; |
| } |
| |
| if let Some(ecn) = ecn_counts { |
| len += octets::varint_len(ecn.ect0_count) + |
| octets::varint_len(ecn.ect1_count) + |
| octets::varint_len(ecn.ecn_ce_count); |
| } |
| |
| len |
| }, |
| |
| Frame::ResetStream { |
| stream_id, |
| error_code, |
| final_size, |
| } => { |
| 1 + // frame type |
| octets::varint_len(*stream_id) + // stream_id |
| octets::varint_len(*error_code) + // error_code |
| octets::varint_len(*final_size) // final_size |
| }, |
| |
| Frame::StopSending { |
| stream_id, |
| error_code, |
| } => { |
| 1 + // frame type |
| octets::varint_len(*stream_id) + // stream_id |
| octets::varint_len(*error_code) // error_code |
| }, |
| |
| Frame::Crypto { data } => { |
| 1 + // frame type |
| octets::varint_len(data.off()) + // offset |
| 2 + // length, always encode as 2-byte varint |
| data.len() // data |
| }, |
| |
| Frame::CryptoHeader { offset, length, .. } => { |
| 1 + // frame type |
| octets::varint_len(*offset) + // offset |
| 2 + // length, always encode as 2-byte varint |
| length // data |
| }, |
| |
| Frame::NewToken { token } => { |
| 1 + // frame type |
| octets::varint_len(token.len() as u64) + // token length |
| token.len() // token |
| }, |
| |
| Frame::Stream { stream_id, data } => { |
| 1 + // frame type |
| octets::varint_len(*stream_id) + // stream_id |
| octets::varint_len(data.off()) + // offset |
| 2 + // length, always encode as 2-byte varint |
| data.len() // data |
| }, |
| |
| Frame::StreamHeader { |
| stream_id, |
| offset, |
| length, |
| .. |
| } => { |
| 1 + // frame type |
| octets::varint_len(*stream_id) + // stream_id |
| octets::varint_len(*offset) + // offset |
| 2 + // length, always encode as 2-byte varint |
| length // data |
| }, |
| |
| Frame::MaxData { max } => { |
| 1 + // frame type |
| octets::varint_len(*max) // max |
| }, |
| |
| Frame::MaxStreamData { stream_id, max } => { |
| 1 + // frame type |
| octets::varint_len(*stream_id) + // stream_id |
| octets::varint_len(*max) // max |
| }, |
| |
| Frame::MaxStreamsBidi { max } => { |
| 1 + // frame type |
| octets::varint_len(*max) // max |
| }, |
| |
| Frame::MaxStreamsUni { max } => { |
| 1 + // frame type |
| octets::varint_len(*max) // max |
| }, |
| |
| Frame::DataBlocked { limit } => { |
| 1 + // frame type |
| octets::varint_len(*limit) // limit |
| }, |
| |
| Frame::StreamDataBlocked { stream_id, limit } => { |
| 1 + // frame type |
| octets::varint_len(*stream_id) + // stream_id |
| octets::varint_len(*limit) // limit |
| }, |
| |
| Frame::StreamsBlockedBidi { limit } => { |
| 1 + // frame type |
| octets::varint_len(*limit) // limit |
| }, |
| |
| Frame::StreamsBlockedUni { limit } => { |
| 1 + // frame type |
| octets::varint_len(*limit) // limit |
| }, |
| |
| Frame::NewConnectionId { |
| seq_num, |
| retire_prior_to, |
| conn_id, |
| reset_token, |
| } => { |
| 1 + // frame type |
| octets::varint_len(*seq_num) + // seq_num |
| octets::varint_len(*retire_prior_to) + // retire_prior_to |
| 1 + // conn_id length |
| conn_id.len() + // conn_id |
| reset_token.len() // reset_token |
| }, |
| |
| Frame::RetireConnectionId { seq_num } => { |
| 1 + // frame type |
| octets::varint_len(*seq_num) // seq_num |
| }, |
| |
| Frame::PathChallenge { .. } => { |
| 1 + // frame type |
| 8 // data |
| }, |
| |
| Frame::PathResponse { .. } => { |
| 1 + // frame type |
| 8 // data |
| }, |
| |
| Frame::ConnectionClose { |
| frame_type, |
| error_code, |
| reason, |
| .. |
| } => { |
| 1 + // frame type |
| octets::varint_len(*error_code) + // error_code |
| octets::varint_len(*frame_type) + // frame_type |
| octets::varint_len(reason.len() as u64) + // reason_len |
| reason.len() // reason |
| }, |
| |
| Frame::ApplicationClose { reason, error_code } => { |
| 1 + // frame type |
| octets::varint_len(*error_code) + // error_code |
| octets::varint_len(reason.len() as u64) + // reason_len |
| reason.len() // reason |
| }, |
| |
| Frame::HandshakeDone => { |
| 1 // frame type |
| }, |
| |
| Frame::Datagram { data } => { |
| 1 + // frame type |
| 2 + // length, always encode as 2-byte varint |
| data.len() // data |
| }, |
| |
| Frame::DatagramHeader { length } => { |
| 1 + // frame type |
| 2 + // length, always encode as 2-byte varint |
| *length // data |
| }, |
| } |
| } |
| |
| pub fn ack_eliciting(&self) -> bool { |
| // Any other frame is ack-eliciting (note the `!`). |
| !matches!( |
| self, |
| Frame::Padding { .. } | |
| Frame::ACK { .. } | |
| Frame::ApplicationClose { .. } | |
| Frame::ConnectionClose { .. } |
| ) |
| } |
| |
| pub fn probing(&self) -> bool { |
| matches!( |
| self, |
| Frame::Padding { .. } | |
| Frame::NewConnectionId { .. } | |
| Frame::PathChallenge { .. } | |
| Frame::PathResponse { .. } |
| ) |
| } |
| |
| #[cfg(feature = "qlog")] |
| pub fn to_qlog(&self) -> QuicFrame { |
| match self { |
| Frame::Padding { len } => QuicFrame::Padding { |
| length: None, |
| payload_length: *len as u32, |
| }, |
| |
| Frame::Ping { .. } => QuicFrame::Ping { |
| length: None, |
| payload_length: None, |
| }, |
| |
| Frame::ACK { |
| ack_delay, |
| ranges, |
| ecn_counts, |
| } => { |
| let ack_ranges = AckedRanges::Double( |
| ranges.iter().map(|r| (r.start, r.end - 1)).collect(), |
| ); |
| |
| let (ect0, ect1, ce) = match ecn_counts { |
| Some(ecn) => ( |
| Some(ecn.ect0_count), |
| Some(ecn.ect1_count), |
| Some(ecn.ecn_ce_count), |
| ), |
| |
| None => (None, None, None), |
| }; |
| |
| QuicFrame::Ack { |
| ack_delay: Some(*ack_delay as f32 / 1000.0), |
| acked_ranges: Some(ack_ranges), |
| ect1, |
| ect0, |
| ce, |
| length: None, |
| payload_length: None, |
| } |
| }, |
| |
| Frame::ResetStream { |
| stream_id, |
| error_code, |
| final_size, |
| } => QuicFrame::ResetStream { |
| stream_id: *stream_id, |
| error_code: *error_code, |
| final_size: *final_size, |
| length: None, |
| payload_length: None, |
| }, |
| |
| Frame::StopSending { |
| stream_id, |
| error_code, |
| } => QuicFrame::StopSending { |
| stream_id: *stream_id, |
| error_code: *error_code, |
| length: None, |
| payload_length: None, |
| }, |
| |
| Frame::Crypto { data } => QuicFrame::Crypto { |
| offset: data.off(), |
| length: data.len() as u64, |
| }, |
| |
| Frame::CryptoHeader { offset, length } => QuicFrame::Crypto { |
| offset: *offset, |
| length: *length as u64, |
| }, |
| |
| Frame::NewToken { token } => QuicFrame::NewToken { |
| token: qlog::Token { |
| // TODO: pick the token type some how |
| ty: Some(qlog::TokenType::Retry), |
| raw: Some(qlog::events::RawInfo { |
| data: qlog::HexSlice::maybe_string(Some(token)), |
| length: Some(token.len() as u64), |
| payload_length: None, |
| }), |
| details: None, |
| }, |
| }, |
| |
| Frame::Stream { stream_id, data } => QuicFrame::Stream { |
| stream_id: *stream_id, |
| offset: data.off(), |
| length: data.len() as u64, |
| fin: data.fin().then_some(true), |
| raw: None, |
| }, |
| |
| Frame::StreamHeader { |
| stream_id, |
| offset, |
| length, |
| fin, |
| } => QuicFrame::Stream { |
| stream_id: *stream_id, |
| offset: *offset, |
| length: *length as u64, |
| fin: fin.then(|| true), |
| raw: None, |
| }, |
| |
| Frame::MaxData { max } => QuicFrame::MaxData { maximum: *max }, |
| |
| Frame::MaxStreamData { stream_id, max } => QuicFrame::MaxStreamData { |
| stream_id: *stream_id, |
| maximum: *max, |
| }, |
| |
| Frame::MaxStreamsBidi { max } => QuicFrame::MaxStreams { |
| stream_type: StreamType::Bidirectional, |
| maximum: *max, |
| }, |
| |
| Frame::MaxStreamsUni { max } => QuicFrame::MaxStreams { |
| stream_type: StreamType::Unidirectional, |
| maximum: *max, |
| }, |
| |
| Frame::DataBlocked { limit } => |
| QuicFrame::DataBlocked { limit: *limit }, |
| |
| Frame::StreamDataBlocked { stream_id, limit } => |
| QuicFrame::StreamDataBlocked { |
| stream_id: *stream_id, |
| limit: *limit, |
| }, |
| |
| Frame::StreamsBlockedBidi { limit } => QuicFrame::StreamsBlocked { |
| stream_type: StreamType::Bidirectional, |
| limit: *limit, |
| }, |
| |
| Frame::StreamsBlockedUni { limit } => QuicFrame::StreamsBlocked { |
| stream_type: StreamType::Unidirectional, |
| limit: *limit, |
| }, |
| |
| Frame::NewConnectionId { |
| seq_num, |
| retire_prior_to, |
| conn_id, |
| reset_token, |
| } => QuicFrame::NewConnectionId { |
| sequence_number: *seq_num as u32, |
| retire_prior_to: *retire_prior_to as u32, |
| connection_id_length: Some(conn_id.len() as u8), |
| connection_id: format!("{}", qlog::HexSlice::new(conn_id)), |
| stateless_reset_token: qlog::HexSlice::maybe_string(Some( |
| reset_token, |
| )), |
| }, |
| |
| Frame::RetireConnectionId { seq_num } => |
| QuicFrame::RetireConnectionId { |
| sequence_number: *seq_num as u32, |
| }, |
| |
| Frame::PathChallenge { .. } => |
| QuicFrame::PathChallenge { data: None }, |
| |
| Frame::PathResponse { .. } => QuicFrame::PathResponse { data: None }, |
| |
| Frame::ConnectionClose { |
| error_code, reason, .. |
| } => QuicFrame::ConnectionClose { |
| error_space: Some(ErrorSpace::TransportError), |
| error_code: Some(*error_code), |
| error_code_value: None, // raw error is no different for us |
| reason: Some(String::from_utf8_lossy(reason).into_owned()), |
| trigger_frame_type: None, // don't know trigger type |
| }, |
| |
| Frame::ApplicationClose { error_code, reason } => |
| QuicFrame::ConnectionClose { |
| error_space: Some(ErrorSpace::ApplicationError), |
| error_code: Some(*error_code), |
| error_code_value: None, // raw error is no different for us |
| reason: Some(String::from_utf8_lossy(reason).into_owned()), |
| trigger_frame_type: None, // don't know trigger type |
| }, |
| |
| Frame::HandshakeDone => QuicFrame::HandshakeDone, |
| |
| Frame::Datagram { data } => QuicFrame::Datagram { |
| length: data.len() as u64, |
| raw: None, |
| }, |
| |
| Frame::DatagramHeader { length } => QuicFrame::Datagram { |
| length: *length as u64, |
| raw: None, |
| }, |
| } |
| } |
| } |
| |
| impl std::fmt::Debug for Frame { |
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
| match self { |
| Frame::Padding { len } => { |
| write!(f, "PADDING len={len}")?; |
| }, |
| |
| Frame::Ping { mtu_probe } => { |
| write!(f, "PING mtu_probe={mtu_probe:?}")?; |
| }, |
| |
| Frame::ACK { |
| ack_delay, |
| ranges, |
| ecn_counts, |
| } => { |
| write!( |
| f, |
| "ACK delay={ack_delay} blocks={ranges:?} ecn_counts={ecn_counts:?}" |
| )?; |
| }, |
| |
| Frame::ResetStream { |
| stream_id, |
| error_code, |
| final_size, |
| } => { |
| write!( |
| f, |
| "RESET_STREAM stream={stream_id} err={error_code:x} size={final_size}" |
| )?; |
| }, |
| |
| Frame::StopSending { |
| stream_id, |
| error_code, |
| } => { |
| write!(f, "STOP_SENDING stream={stream_id} err={error_code:x}")?; |
| }, |
| |
| Frame::Crypto { data } => { |
| write!(f, "CRYPTO off={} len={}", data.off(), data.len())?; |
| }, |
| |
| Frame::CryptoHeader { offset, length } => { |
| write!(f, "CRYPTO off={offset} len={length}")?; |
| }, |
| |
| Frame::NewToken { token } => { |
| write!(f, "NEW_TOKEN len={}", token.len())?; |
| }, |
| |
| Frame::Stream { stream_id, data } => { |
| write!( |
| f, |
| "STREAM id={} off={} len={} fin={}", |
| stream_id, |
| data.off(), |
| data.len(), |
| data.fin() |
| )?; |
| }, |
| |
| Frame::StreamHeader { |
| stream_id, |
| offset, |
| length, |
| fin, |
| } => { |
| write!( |
| f, |
| "STREAM id={stream_id} off={offset} len={length} fin={fin}" |
| )?; |
| }, |
| |
| Frame::MaxData { max } => { |
| write!(f, "MAX_DATA max={max}")?; |
| }, |
| |
| Frame::MaxStreamData { stream_id, max } => { |
| write!(f, "MAX_STREAM_DATA stream={stream_id} max={max}")?; |
| }, |
| |
| Frame::MaxStreamsBidi { max } => { |
| write!(f, "MAX_STREAMS type=bidi max={max}")?; |
| }, |
| |
| Frame::MaxStreamsUni { max } => { |
| write!(f, "MAX_STREAMS type=uni max={max}")?; |
| }, |
| |
| Frame::DataBlocked { limit } => { |
| write!(f, "DATA_BLOCKED limit={limit}")?; |
| }, |
| |
| Frame::StreamDataBlocked { stream_id, limit } => { |
| write!( |
| f, |
| "STREAM_DATA_BLOCKED stream={stream_id} limit={limit}" |
| )?; |
| }, |
| |
| Frame::StreamsBlockedBidi { limit } => { |
| write!(f, "STREAMS_BLOCKED type=bidi limit={limit}")?; |
| }, |
| |
| Frame::StreamsBlockedUni { limit } => { |
| write!(f, "STREAMS_BLOCKED type=uni limit={limit}")?; |
| }, |
| |
| Frame::NewConnectionId { |
| seq_num, |
| retire_prior_to, |
| conn_id, |
| reset_token, |
| } => { |
| write!( |
| f, |
| "NEW_CONNECTION_ID seq_num={seq_num} retire_prior_to={retire_prior_to} conn_id={conn_id:02x?} reset_token={reset_token:02x?}", |
| )?; |
| }, |
| |
| Frame::RetireConnectionId { seq_num } => { |
| write!(f, "RETIRE_CONNECTION_ID seq_num={seq_num}")?; |
| }, |
| |
| Frame::PathChallenge { data } => { |
| write!(f, "PATH_CHALLENGE data={data:02x?}")?; |
| }, |
| |
| Frame::PathResponse { data } => { |
| write!(f, "PATH_RESPONSE data={data:02x?}")?; |
| }, |
| |
| Frame::ConnectionClose { |
| error_code, |
| frame_type, |
| reason, |
| } => { |
| write!( |
| f, |
| "CONNECTION_CLOSE err={error_code:x} frame={frame_type:x} reason={reason:x?}" |
| )?; |
| }, |
| |
| Frame::ApplicationClose { error_code, reason } => { |
| write!( |
| f, |
| "APPLICATION_CLOSE err={error_code:x} reason={reason:x?}" |
| )?; |
| }, |
| |
| Frame::HandshakeDone => { |
| write!(f, "HANDSHAKE_DONE")?; |
| }, |
| |
| Frame::Datagram { data } => { |
| write!(f, "DATAGRAM len={}", data.len())?; |
| }, |
| |
| Frame::DatagramHeader { length } => { |
| write!(f, "DATAGRAM len={length}")?; |
| }, |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| fn parse_ack_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> { |
| let first = ty as u8; |
| |
| let largest_ack = b.get_varint()?; |
| let ack_delay = b.get_varint()?; |
| let block_count = b.get_varint()?; |
| let ack_block = b.get_varint()?; |
| |
| if largest_ack < ack_block { |
| return Err(Error::InvalidFrame); |
| } |
| |
| let mut smallest_ack = largest_ack - ack_block; |
| |
| let mut ranges = ranges::RangeSet::default(); |
| |
| ranges.insert(smallest_ack..largest_ack + 1); |
| |
| for _i in 0..block_count { |
| let gap = b.get_varint()?; |
| |
| if smallest_ack < 2 + gap { |
| return Err(Error::InvalidFrame); |
| } |
| |
| let largest_ack = (smallest_ack - gap) - 2; |
| let ack_block = b.get_varint()?; |
| |
| if largest_ack < ack_block { |
| return Err(Error::InvalidFrame); |
| } |
| |
| smallest_ack = largest_ack - ack_block; |
| |
| ranges.insert(smallest_ack..largest_ack + 1); |
| } |
| |
| let ecn_counts = if first & 0x01 != 0 { |
| let ecn = EcnCounts { |
| ect0_count: b.get_varint()?, |
| ect1_count: b.get_varint()?, |
| ecn_ce_count: b.get_varint()?, |
| }; |
| |
| Some(ecn) |
| } else { |
| None |
| }; |
| |
| Ok(Frame::ACK { |
| ack_delay, |
| ranges, |
| ecn_counts, |
| }) |
| } |
| |
| pub fn encode_crypto_header( |
| offset: u64, length: u64, b: &mut octets::OctetsMut, |
| ) -> Result<()> { |
| b.put_varint(0x06)?; |
| |
| b.put_varint(offset)?; |
| |
| // Always encode length field as 2-byte varint. |
| b.put_varint_with_len(length, 2)?; |
| |
| Ok(()) |
| } |
| |
| pub fn encode_stream_header( |
| stream_id: u64, offset: u64, length: u64, fin: bool, |
| b: &mut octets::OctetsMut, |
| ) -> Result<()> { |
| let mut ty: u8 = 0x08; |
| |
| // Always encode offset. |
| ty |= 0x04; |
| |
| // Always encode length. |
| ty |= 0x02; |
| |
| if fin { |
| ty |= 0x01; |
| } |
| |
| b.put_varint(u64::from(ty))?; |
| |
| b.put_varint(stream_id)?; |
| b.put_varint(offset)?; |
| |
| // Always encode length field as 2-byte varint. |
| b.put_varint_with_len(length, 2)?; |
| |
| Ok(()) |
| } |
| |
| pub fn encode_dgram_header(length: u64, b: &mut octets::OctetsMut) -> Result<()> { |
| let mut ty: u8 = 0x30; |
| |
| // Always encode length |
| ty |= 0x01; |
| |
| b.put_varint(u64::from(ty))?; |
| |
| // Always encode length field as 2-byte varint. |
| b.put_varint_with_len(length, 2)?; |
| |
| Ok(()) |
| } |
| |
| fn parse_stream_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> { |
| let first = ty as u8; |
| |
| let stream_id = b.get_varint()?; |
| |
| let offset = if first & 0x04 != 0 { |
| b.get_varint()? |
| } else { |
| 0 |
| }; |
| |
| let len = if first & 0x02 != 0 { |
| b.get_varint()? as usize |
| } else { |
| b.cap() |
| }; |
| |
| if offset + len as u64 >= MAX_STREAM_SIZE { |
| return Err(Error::InvalidFrame); |
| } |
| |
| let fin = first & 0x01 != 0; |
| |
| let data = b.get_bytes(len)?; |
| let data = stream::RangeBuf::from(data.as_ref(), offset, fin); |
| |
| Ok(Frame::Stream { stream_id, data }) |
| } |
| |
| fn parse_datagram_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> { |
| let first = ty as u8; |
| |
| let len = if first & 0x01 != 0 { |
| b.get_varint()? as usize |
| } else { |
| b.cap() |
| }; |
| |
| let data = b.get_bytes(len)?; |
| |
| Ok(Frame::Datagram { |
| data: Vec::from(data.buf()), |
| }) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn padding() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::Padding { len: 128 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 128); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok()); |
| } |
| |
| #[test] |
| fn ping() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::Ping { mtu_probe: None }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 1); |
| assert_eq!(&d[..wire_len], [0x01_u8]); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok()); |
| } |
| |
| #[test] |
| fn ack() { |
| let mut d = [42; 128]; |
| |
| let mut ranges = ranges::RangeSet::default(); |
| ranges.insert(4..7); |
| ranges.insert(9..12); |
| ranges.insert(15..19); |
| ranges.insert(3000..5000); |
| |
| let frame = Frame::ACK { |
| ack_delay: 874_656_534, |
| ranges, |
| ecn_counts: None, |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 17); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok()); |
| } |
| |
| #[test] |
| fn ack_ecn() { |
| let mut d = [42; 128]; |
| |
| let mut ranges = ranges::RangeSet::default(); |
| ranges.insert(4..7); |
| ranges.insert(9..12); |
| ranges.insert(15..19); |
| ranges.insert(3000..5000); |
| |
| let ecn_counts = Some(EcnCounts { |
| ect0_count: 100, |
| ect1_count: 200, |
| ecn_ce_count: 300, |
| }); |
| |
| let frame = Frame::ACK { |
| ack_delay: 874_656_534, |
| ranges, |
| ecn_counts, |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 23); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok()); |
| } |
| |
| #[test] |
| fn reset_stream() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::ResetStream { |
| stream_id: 123_213, |
| error_code: 21_123_767, |
| final_size: 21_123_767, |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 13); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn stop_sending() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::StopSending { |
| stream_id: 123_213, |
| error_code: 15_352, |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 7); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn crypto() { |
| let mut d = [42; 128]; |
| |
| let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; |
| |
| let frame = Frame::Crypto { |
| data: stream::RangeBuf::from(&data, 1230976, false), |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 19); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok()); |
| } |
| |
| #[test] |
| fn new_token() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::NewToken { |
| token: Vec::from("this is a token"), |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 17); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn stream() { |
| let mut d = [42; 128]; |
| |
| let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; |
| |
| let frame = Frame::Stream { |
| stream_id: 32, |
| data: stream::RangeBuf::from(&data, 1230976, true), |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 20); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn stream_too_big() { |
| let mut d = [42; 128]; |
| |
| let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; |
| |
| let frame = Frame::Stream { |
| stream_id: 32, |
| data: stream::RangeBuf::from(&data, MAX_STREAM_SIZE - 11, true), |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 24); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!( |
| Frame::from_bytes(&mut b, packet::Type::Short), |
| Err(Error::InvalidFrame) |
| ); |
| } |
| |
| #[test] |
| fn max_data() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::MaxData { max: 128_318_273 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn max_stream_data() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::MaxStreamData { |
| stream_id: 12_321, |
| max: 128_318_273, |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 7); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn max_streams_bidi() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::MaxStreamsBidi { max: 128_318_273 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn max_streams_uni() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::MaxStreamsUni { max: 128_318_273 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn data_blocked() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::DataBlocked { limit: 128_318_273 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn stream_data_blocked() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::StreamDataBlocked { |
| stream_id: 12_321, |
| limit: 128_318_273, |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 7); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn streams_blocked_bidi() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::StreamsBlockedBidi { limit: 128_318_273 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn streams_blocked_uni() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::StreamsBlockedUni { limit: 128_318_273 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn new_connection_id() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::NewConnectionId { |
| seq_num: 123_213, |
| retire_prior_to: 122_211, |
| conn_id: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], |
| reset_token: [0x42; 16], |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 41); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn retire_connection_id() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::RetireConnectionId { seq_num: 123_213 }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 5); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn path_challenge() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::PathChallenge { |
| data: [1, 2, 3, 4, 5, 6, 7, 8], |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 9); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn path_response() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::PathResponse { |
| data: [1, 2, 3, 4, 5, 6, 7, 8], |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 9); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn connection_close() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::ConnectionClose { |
| error_code: 0xbeef, |
| frame_type: 523_423, |
| reason: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 22); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok()); |
| } |
| |
| #[test] |
| fn application_close() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::ApplicationClose { |
| error_code: 0xbeef, |
| reason: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], |
| }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 18); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn handshake_done() { |
| let mut d = [42; 128]; |
| |
| let frame = Frame::HandshakeDone; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 1); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| } |
| |
| #[test] |
| fn datagram() { |
| let mut d = [42; 128]; |
| |
| let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; |
| |
| let frame = Frame::Datagram { data: data.clone() }; |
| |
| let wire_len = { |
| let mut b = octets::OctetsMut::with_slice(&mut d); |
| frame.to_bytes(&mut b).unwrap() |
| }; |
| |
| assert_eq!(wire_len, 15); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert_eq!( |
| Frame::from_bytes(&mut b, packet::Type::Short), |
| Ok(frame.clone()) |
| ); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_err()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_ok()); |
| |
| let mut b = octets::Octets::with_slice(&d); |
| assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_err()); |
| |
| let frame_data = match &frame { |
| Frame::Datagram { data } => data.clone(), |
| |
| _ => unreachable!(), |
| }; |
| |
| assert_eq!(frame_data, data); |
| } |
| } |