rtc_sctp/lib.rs
1//! SCTP for the Sans-I/O WebRTC stack.
2//!
3//! The Stream Control Transmission Protocol ([RFC 4960]) with the extensions WebRTC data
4//! channels need: partial reliability ([RFC 3758]) and stream reset / reconfiguration
5//! ([RFC 6525]). In WebRTC, SCTP runs *over* DTLS rather than over IP, and carries the data
6//! channels described by [`rtc-datachannel`].
7//!
8//! This is a fully deterministic implementation of the protocol logic. It contains no
9//! networking code and reads no clock of its own: you feed it datagrams and time, and poll it
10//! for the datagrams and events it produces. That is what makes it testable without a network
11//! and reusable under any executor.
12//!
13//! # Structure
14//!
15//! * [`Endpoint`] — the protocol state for one socket. It holds configuration and dispatches
16//! inbound datagrams to the right association.
17//! * [`Association`] — the bulk of the logic for a single
18//! association: handshake, congestion control, retransmission, and its streams.
19//! * [`Stream`] — one stream's reads, writes and reliability
20//! settings.
21//! * [`Chunks`] — a reassembled inbound message, delivered once every fragment has arrived.
22//!
23//! # Example
24//!
25//! Configuration is plain data, and the association is driven entirely by the caller — feed it
26//! datagrams and time, poll it for output:
27//!
28//! ```
29//! use rtc_sctp::{EndpointConfig, TransportConfig};
30//! use std::sync::Arc;
31//!
32//! let transport = TransportConfig::default()
33//! .with_max_message_size(65_536)
34//! .with_max_num_outbound_streams(1024);
35//!
36//! let endpoint_config = Arc::new(EndpointConfig::new());
37//! assert_eq!(transport.max_message_size(), 65_536);
38//! # let _ = endpoint_config;
39//! ```
40//!
41//! Most applications do not depend on this crate directly — the [`rtc`](https://docs.rs/rtc)
42//! crate drives it as one layer of the peer-connection pipeline and exposes data channels,
43//! and [`webrtc`](https://docs.rs/webrtc) wraps that in an async API.
44//!
45//! [RFC 4960]: https://datatracker.ietf.org/doc/html/rfc4960
46//! [RFC 3758]: https://datatracker.ietf.org/doc/html/rfc3758
47//! [RFC 6525]: https://datatracker.ietf.org/doc/html/rfc6525
48//! [`rtc-datachannel`]: https://docs.rs/rtc-datachannel
49
50#![warn(rust_2018_idioms)]
51#![warn(missing_docs)]
52#![allow(dead_code)]
53#![allow(clippy::bool_to_int_with_if)]
54
55use bytes::Bytes;
56use std::{fmt, ops};
57
58mod association;
59pub use crate::association::{
60 Association, AssociationError, Event,
61 stats::AssociationStats,
62 stream::{ReliabilityType, Stream, StreamEvent, StreamId, StreamState},
63 timer::TimerConfig,
64};
65
66pub(crate) mod chunk;
67pub use crate::chunk::{
68 ErrorCauseCode,
69 chunk_payload_data::{ChunkPayloadData, PayloadProtocolIdentifier},
70};
71
72mod config;
73pub use crate::config::{ClientConfig, EndpointConfig, ServerConfig, TransportConfig};
74
75mod endpoint;
76pub use crate::endpoint::{AssociationHandle, ConnectError, DatagramEvent, Endpoint};
77
78mod packet;
79
80mod shared;
81pub use crate::shared::{AssociationEvent, AssociationId, EndpointEvent};
82
83pub(crate) mod param;
84
85pub(crate) mod queue;
86pub use crate::queue::reassembly_queue::{Chunk, Chunks};
87
88pub(crate) mod util;
89
90/// Entry points for fuzz targets and benchmarks.
91///
92/// Thin wrappers that drive one encode or decode step over a raw byte slice, so a fuzzer or
93/// a benchmark can reach the packet codec without setting up an association. Gated behind
94/// `cfg(fuzzing)` or the `bench` feature; not part of the supported API.
95#[cfg(any(fuzzing, feature = "bench"))]
96pub mod fuzzing;
97
98/// Whether an endpoint was the initiator of an association
99#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
100pub enum Side {
101 /// The initiator of an association
102 #[default]
103 Client = 0,
104 /// The acceptor of an association
105 Server = 1,
106}
107
108impl fmt::Display for Side {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 let s = match *self {
111 Side::Client => "Client",
112 Side::Server => "Server",
113 };
114 write!(f, "{}", s)
115 }
116}
117
118impl Side {
119 #[inline]
120 /// Shorthand for `self == Side::Client`
121 pub fn is_client(self) -> bool {
122 self == Side::Client
123 }
124
125 #[inline]
126 /// Shorthand for `self == Side::Server`
127 pub fn is_server(self) -> bool {
128 self == Side::Server
129 }
130}
131
132impl ops::Not for Side {
133 type Output = Side;
134 fn not(self) -> Side {
135 match self {
136 Side::Client => Side::Server,
137 Side::Server => Side::Client,
138 }
139 }
140}
141
142use crate::packet::PartialDecode;
143
144/// Payload in Incoming/outgoing Transmit
145#[derive(Debug)]
146pub enum Payload {
147 /// An inbound packet whose header has been decoded but whose chunks have not.
148 PartialDecode(PartialDecode),
149 /// Outbound packets, already encoded and ready to hand to the transport.
150 RawEncode(Vec<Bytes>),
151}