Skip to content
File

Blob: firmware/vendor/sctp-proto/src/lib.rs

rust219 lines
1//! Low-level protocol logic for the SCTP protocol
2//!
3//! sctp-proto contains a fully deterministic implementation of SCTP protocol logic. It contains
4//! no networking code and does not get any relevant timestamps from the operating system. Most
5//! users may want to use the futures-based sctp-async API instead.
6//!
7//! The main entry point is [Endpoint], which manages associations for a single socket. Use
8//! [Endpoint::connect] to initiate outgoing associations, or provide a [ServerConfig] to
9//! accept incoming ones. Incoming UDP datagrams are fed to [Endpoint::handle], which either
10//! creates a new [Association] or returns an event to pass to an existing one.
11//!
12//! [Association] holds the protocol state for a single SCTP association. It produces
13//! [Event]s and outgoing packets via polling methods ([Association::poll],
14//! [Association::poll_transmit]). Each association contains multiple [Stream]s for
15//! reading and writing data.
16//!
17//! [Endpoint]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.Endpoint.html
18//! [Endpoint::connect]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.Endpoint.html#method.connect
19//! [Endpoint::handle]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.Endpoint.html#method.handle
20//! [ServerConfig]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.ServerConfig.html
21//! [Association]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.Association.html
22//! [Association::poll]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.Association.html#method.poll
23//! [Association::poll_transmit]:
24//! https://docs.rs/sctp-proto/latest/sctp_proto/struct.Association.html#method.poll_transmit
25//! [Event]: https://docs.rs/sctp-proto/latest/sctp_proto/enum.Event.html
26//! [Stream]: https://docs.rs/sctp-proto/latest/sctp_proto/struct.Stream.html
27//!
28//! ## Status
29//!
30//! This crate is maintained by the [str0m] project, which has been using it since
31//! January 2023. Other consumers include [ex_sctp] for Elixir WebRTC. The crate
32//! is kept in sync with [rtc-sctp] where possible to share bug fixes.
33//!
34//! Originally written by Rain Liu as a Sans-IO implementation of SCTP for the
35//! webrtc-rs ecosystem, this crate predates `rtc-sctp` in the `webrtc-rs/rtc`
36//! monorepo, which was later derived from this work. Maintenance was transferred
37//! to the str0m maintainers in January 2026.
38//!
39//! [str0m]: https://crates.io/crates/str0m
40//! [ex_sctp]: https://github.com/elixir-webrtc/ex_sctp
41//! [rtc-sctp]: https://github.com/webrtc-rs/rtc/tree/master/rtc-sctp
42 
43#![no_std]
44#![warn(rust_2018_idioms)]
45#![deny(clippy::std_instead_of_core)]
46#![deny(clippy::std_instead_of_alloc)]
47#![allow(dead_code)]
48#![allow(clippy::bool_to_int_with_if)]
49#![forbid(unsafe_code)]
50 
51#[macro_use]
52extern crate alloc;
53 
54extern crate std;
55 
56use alloc::vec::Vec;
57use bytes::Bytes;
58use core::fmt;
59use core::net::{IpAddr, SocketAddr};
60use core::ops;
61use std::time::Instant;
62 
63mod association;
64pub use crate::association::Association;
65pub use crate::association::AssociationError;
66pub use crate::association::Event;
67pub use crate::association::stats::AssociationStats;
68pub use crate::association::stream::{
69 ReliabilityType, Stream, StreamEvent, StreamId, StreamResetError, StreamState,
70};
71 
72pub(crate) mod chunk;
73pub use crate::chunk::ErrorCauseCode;
74pub use crate::chunk::chunk_payload_data::{ChunkPayloadData, PayloadProtocolIdentifier};
75 
76mod config;
77pub use crate::config::{
78 ClientConfig, DEFAULT_SCTP_PORT, EndpointConfig, MAX_SNAP_INIT_BYTES, ReceiveLimits,
79 ServerConfig, TransportConfig, generate_snap_token,
80};
81 
82mod endpoint;
83pub use crate::endpoint::{
84 AidCollisionKind, AssociationHandle, ConnectError, DatagramEvent, Endpoint, SnapError, SnapSide,
85};
86 
87mod error;
88pub use crate::error::Error;
89 
90mod packet;
91 
92mod shared;
93pub use crate::shared::{AssociationEvent, AssociationId, EcnCodepoint, EndpointEvent};
94 
95pub(crate) mod param;
96 
97pub(crate) mod queue;
98pub use crate::queue::reassembly_queue::{Chunk, Chunks};
99 
100pub(crate) mod util;
101 
102/// Fuzz helpers. Not part of the public API.
103#[cfg(feature = "_fuzz")]
104pub mod _fuzz {
105 use bytes::BytesMut;
106 
107 use crate::packet::Packet;
108 use crate::util::generate_packet_checksum;
109 
110 /// Feed arbitrary bytes into packet unmarshal.
111 ///
112 /// Patches a valid CRC32C checksum so the fuzzer can
113 /// reach the parsing logic beyond the checksum check.
114 pub fn fuzz_packet_unmarshal(data: &[u8]) {
115 if data.len() < 12 {
116 return;
117 }
118 let mut buf = BytesMut::from(data);
119 // Zero checksum field before computing
120 buf[8] = 0;
121 buf[9] = 0;
122 buf[10] = 0;
123 buf[11] = 0;
124 let raw = buf.freeze();
125 let checksum = generate_packet_checksum(&raw);
126 let mut buf = BytesMut::from(raw.as_ref());
127 buf[8..12].copy_from_slice(&checksum.to_le_bytes());
128 let raw = buf.freeze();
129 let _ = Packet::unmarshal(&raw);
130 }
131}
132 
133/// Whether an endpoint was the initiator of an association
134#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
135pub enum Side {
136 /// The initiator of an association
137 #[default]
138 Client = 0,
139 /// The acceptor of an association
140 Server = 1,
141}
142 
143impl fmt::Display for Side {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 let s = match *self {
146 Side::Client => "Client",
147 Side::Server => "Server",
148 };
149 write!(f, "{}", s)
150 }
151}
152 
153impl Side {
154 #[inline]
155 /// Shorthand for `self == Side::Client`
156 pub fn is_client(self) -> bool {
157 self == Side::Client
158 }
159 
160 #[inline]
161 /// Shorthand for `self == Side::Server`
162 pub fn is_server(self) -> bool {
163 self == Side::Server
164 }
165}
166 
167impl ops::Not for Side {
168 type Output = Side;
169 fn not(self) -> Side {
170 match self {
171 Side::Client => Side::Server,
172 Side::Server => Side::Client,
173 }
174 }
175}
176 
177use crate::packet::PartialDecode;
178 
179/// Payload in Incoming/outgoing Transmit
180#[derive(Debug)]
181pub enum Payload {
182 PartialDecode(PartialDecode),
183 RawEncode(Vec<Bytes>),
184}
185 
186/// Incoming/outgoing Transmit
187#[derive(Debug)]
188pub struct Transmit {
189 /// Received/Sent time
190 pub now: Instant,
191 /// The socket this datagram should be sent to
192 pub remote: SocketAddr,
193 /// Explicit congestion notification bits to set on the packet
194 pub ecn: Option<EcnCodepoint>,
195 /// Optional local IP address for the datagram
196 pub local_ip: Option<IpAddr>,
197 /// Payload of the datagram
198 pub payload: Payload,
199}
200 
201#[cfg(test)]
202mod test {
203 use alloc::sync::Arc;
204 
205 use super::*;
206 
207 #[test]
208 fn ensure_send_sync() {
209 fn is_send_sync(_a: impl Send + Sync) {}
210 
211 let c = EndpointConfig::new();
212 let e = Endpoint::new(Arc::new(c), None);
213 is_send_sync(e);
214 
215 let a = Association::default();
216 is_send_sync(a);
217 }
218}