Skip to main content

rtc_sctp/chunk/
chunk_payload_data.rs

1use super::{chunk_header::*, chunk_type::*, *};
2
3use bytes::{Buf, BufMut, Bytes, BytesMut};
4use std::fmt;
5use std::time::Instant;
6
7pub(crate) const PAYLOAD_DATA_ENDING_FRAGMENT_BITMASK: u8 = 1;
8pub(crate) const PAYLOAD_DATA_BEGINING_FRAGMENT_BITMASK: u8 = 2;
9pub(crate) const PAYLOAD_DATA_UNORDERED_BITMASK: u8 = 4;
10pub(crate) const PAYLOAD_DATA_IMMEDIATE_SACK: u8 = 8;
11pub(crate) const PAYLOAD_DATA_HEADER_SIZE: usize = 12;
12
13/// PayloadProtocolIdentifier is an enum for DataChannel payload types
14/// PayloadProtocolIdentifier enums
15/// <https://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml#sctp-parameters-25>
16#[derive(Default, Debug, Copy, Clone, PartialEq)]
17#[repr(C)]
18pub enum PayloadProtocolIdentifier {
19    /// `WebRTC DCEP` (50): a Data Channel Establishment Protocol control message.
20    Dcep = 50,
21    /// `WebRTC String` (51): a non-empty UTF-8 string message.
22    String = 51,
23    /// `WebRTC Binary` (53): a non-empty binary message.
24    Binary = 53,
25    /// `WebRTC String Empty` (56): an empty string message.
26    ///
27    /// Needed because SCTP cannot carry a zero-length payload.
28    StringEmpty = 56,
29    /// `WebRTC Binary Empty` (57): an empty binary message.
30    BinaryEmpty = 57,
31    #[default]
32    /// An identifier this crate does not recognise.
33    Unknown,
34}
35
36impl fmt::Display for PayloadProtocolIdentifier {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        let s = match *self {
39            PayloadProtocolIdentifier::Dcep => "WebRTC DCEP",
40            PayloadProtocolIdentifier::String => "WebRTC String",
41            PayloadProtocolIdentifier::Binary => "WebRTC Binary",
42            PayloadProtocolIdentifier::StringEmpty => "WebRTC String (Empty)",
43            PayloadProtocolIdentifier::BinaryEmpty => "WebRTC Binary (Empty)",
44            _ => "Unknown Payload Protocol Identifier",
45        };
46        write!(f, "{}", s)
47    }
48}
49
50impl From<u32> for PayloadProtocolIdentifier {
51    fn from(v: u32) -> PayloadProtocolIdentifier {
52        match v {
53            50 => PayloadProtocolIdentifier::Dcep,
54            51 => PayloadProtocolIdentifier::String,
55            53 => PayloadProtocolIdentifier::Binary,
56            56 => PayloadProtocolIdentifier::StringEmpty,
57            57 => PayloadProtocolIdentifier::BinaryEmpty,
58            _ => PayloadProtocolIdentifier::Unknown,
59        }
60    }
61}
62
63/// ChunkPayloadData represents an SCTP Chunk of type DATA
64//
65// 0                   1                   2                   3
66// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
67//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68//|   Type = 0    | Reserved|U|B|E|    Length                     |
69//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70//|                              TSN                              |
71//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72//|      Stream Identifier S      |   Stream Sequence Number n    |
73//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74//|                  Payload Protocol Identifier                  |
75//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76//|                                                               |
77//|                 User Data (seq n of Stream S)                 |
78//|                                                               |
79//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80//
81//
82//An unfragmented user message shall have both the B and E bits set to
83//'1'.  Setting both B and E bits to '0' indicates a middle fragment of
84//a multi-fragment user message, as summarized in the following table:
85//   B E                  Description
86//============================================================
87//|  1 0 | First piece of a fragmented user message          |
88//+----------------------------------------------------------+
89//|  0 0 | Middle piece of a fragmented user message         |
90//+----------------------------------------------------------+
91//|  0 1 | Last piece of a fragmented user message           |
92//+----------------------------------------------------------+
93//|  1 1 | Unfragmented message                              |
94//============================================================
95//|             Table 1: Fragment Description Flags          |
96//============================================================
97#[derive(Debug, Clone)]
98pub struct ChunkPayloadData {
99    pub(crate) unordered: bool,
100    pub(crate) beginning_fragment: bool,
101    pub(crate) ending_fragment: bool,
102    pub(crate) immediate_sack: bool,
103
104    pub(crate) tsn: u32,
105    pub(crate) stream_identifier: u16,
106    pub(crate) stream_sequence_number: u16,
107    pub(crate) payload_type: PayloadProtocolIdentifier,
108    pub(crate) user_data: Bytes,
109
110    /// Whether this data chunk was acknowledged (received by peer)
111    pub(crate) acked: bool,
112    pub(crate) miss_indicator: u32,
113
114    /// Partial-reliability parameters used only by sender
115    pub(crate) since: Option<Instant>,
116    /// number of transmission made for this chunk
117    pub(crate) nsent: u32,
118
119    /// valid only with the first fragment
120    pub(crate) abandoned: bool,
121    /// valid only with the first fragment
122    pub(crate) all_inflight: bool,
123
124    /// Retransmission flag set when T1-RTX timeout occurred and this
125    /// chunk is still in the inflight queue
126    pub(crate) retransmit: bool,
127}
128
129impl Default for ChunkPayloadData {
130    fn default() -> Self {
131        ChunkPayloadData {
132            unordered: false,
133            beginning_fragment: false,
134            ending_fragment: false,
135            immediate_sack: false,
136            tsn: 0,
137            stream_identifier: 0,
138            stream_sequence_number: 0,
139            payload_type: PayloadProtocolIdentifier::default(),
140            user_data: Bytes::new(),
141            acked: false,
142            miss_indicator: 0,
143            since: None,
144            nsent: 0,
145            abandoned: false,
146            all_inflight: false,
147            retransmit: false,
148        }
149    }
150}
151
152/// makes chunkPayloadData printable
153impl fmt::Display for ChunkPayloadData {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        write!(f, "{}\n{}", self.header(), self.tsn)
156    }
157}
158
159impl Chunk for ChunkPayloadData {
160    fn header(&self) -> ChunkHeader {
161        let mut flags: u8 = 0;
162        if self.ending_fragment {
163            flags = 1;
164        }
165        if self.beginning_fragment {
166            flags |= 1 << 1;
167        }
168        if self.unordered {
169            flags |= 1 << 2;
170        }
171        if self.immediate_sack {
172            flags |= 1 << 3;
173        }
174
175        ChunkHeader {
176            typ: CT_PAYLOAD_DATA,
177            flags,
178            value_length: self.value_length() as u16,
179        }
180    }
181
182    fn unmarshal(raw: &Bytes) -> Result<Self> {
183        let header = ChunkHeader::unmarshal(raw)?;
184
185        if header.typ != CT_PAYLOAD_DATA {
186            return Err(Error::ErrChunkTypeNotPayloadData);
187        }
188
189        let immediate_sack = (header.flags & PAYLOAD_DATA_IMMEDIATE_SACK) != 0;
190        let unordered = (header.flags & PAYLOAD_DATA_UNORDERED_BITMASK) != 0;
191        let beginning_fragment = (header.flags & PAYLOAD_DATA_BEGINING_FRAGMENT_BITMASK) != 0;
192        let ending_fragment = (header.flags & PAYLOAD_DATA_ENDING_FRAGMENT_BITMASK) != 0;
193
194        if raw.len() < CHUNK_HEADER_SIZE + PAYLOAD_DATA_HEADER_SIZE {
195            return Err(Error::ErrChunkPayloadSmall);
196        }
197
198        if header.value_length() < PAYLOAD_DATA_HEADER_SIZE {
199            return Err(Error::ErrChunkUnmarshalPayloadData);
200        }
201
202        let reader = &mut raw.slice(CHUNK_HEADER_SIZE..CHUNK_HEADER_SIZE + header.value_length());
203
204        let tsn = reader.get_u32();
205        let stream_identifier = reader.get_u16();
206        let stream_sequence_number = reader.get_u16();
207        let payload_type: PayloadProtocolIdentifier = reader.get_u32().into();
208        let user_data = raw.slice(
209            CHUNK_HEADER_SIZE + PAYLOAD_DATA_HEADER_SIZE..CHUNK_HEADER_SIZE + header.value_length(),
210        );
211
212        Ok(ChunkPayloadData {
213            unordered,
214            beginning_fragment,
215            ending_fragment,
216            immediate_sack,
217            tsn,
218            stream_identifier,
219            stream_sequence_number,
220            payload_type,
221            user_data,
222
223            acked: false,
224            miss_indicator: 0,
225            since: None,
226            nsent: 0,
227            abandoned: false,
228            all_inflight: false,
229            retransmit: false,
230        })
231    }
232
233    fn marshal_to(&self, writer: &mut BytesMut) -> Result<usize> {
234        self.header().marshal_to(writer)?;
235
236        writer.put_u32(self.tsn);
237        writer.put_u16(self.stream_identifier);
238        writer.put_u16(self.stream_sequence_number);
239        writer.put_u32(self.payload_type as u32);
240        // NB: `extend(Bytes)` iterates the payload one byte at a time (Bytes is
241        // `IntoIterator<Item = u8>`); `extend_from_slice` does a single bulk copy.
242        writer.extend_from_slice(&self.user_data);
243
244        Ok(writer.len())
245    }
246
247    fn check(&self) -> Result<()> {
248        Ok(())
249    }
250
251    fn value_length(&self) -> usize {
252        PAYLOAD_DATA_HEADER_SIZE + self.user_data.len()
253    }
254
255    fn as_any(&self) -> &dyn Any {
256        self
257    }
258}
259
260impl ChunkPayloadData {
261    pub(crate) fn abandoned(&self) -> bool {
262        self.abandoned && self.all_inflight
263    }
264
265    pub(crate) fn set_abandoned(&mut self, abandoned: bool) {
266        self.abandoned = abandoned;
267    }
268
269    pub(crate) fn set_all_inflight(&mut self) {
270        if self.ending_fragment {
271            self.all_inflight = true;
272        }
273    }
274}