Skip to main content

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}