File
Blob: firmware/vendor/sctp-proto/src/lib.rs
| 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] |
| 52 | extern crate alloc; |
| 53 | |
| 54 | extern crate std; |
| 55 | |
| 56 | use alloc::vec::Vec; |
| 57 | use bytes::Bytes; |
| 58 | use core::fmt; |
| 59 | use core::net::{IpAddr, SocketAddr}; |
| 60 | use core::ops; |
| 61 | use std::time::Instant; |
| 62 | |
| 63 | mod association; |
| 64 | pub use crate::association::Association; |
| 65 | pub use crate::association::AssociationError; |
| 66 | pub use crate::association::Event; |
| 67 | pub use crate::association::stats::AssociationStats; |
| 68 | pub use crate::association::stream::{ |
| 69 | ReliabilityType, Stream, StreamEvent, StreamId, StreamResetError, StreamState, |
| 70 | }; |
| 71 | |
| 72 | pub(crate) mod chunk; |
| 73 | pub use crate::chunk::ErrorCauseCode; |
| 74 | pub use crate::chunk::chunk_payload_data::{ChunkPayloadData, PayloadProtocolIdentifier}; |
| 75 | |
| 76 | mod config; |
| 77 | pub use crate::config::{ |
| 78 | ClientConfig, DEFAULT_SCTP_PORT, EndpointConfig, MAX_SNAP_INIT_BYTES, ReceiveLimits, |
| 79 | ServerConfig, TransportConfig, generate_snap_token, |
| 80 | }; |
| 81 | |
| 82 | mod endpoint; |
| 83 | pub use crate::endpoint::{ |
| 84 | AidCollisionKind, AssociationHandle, ConnectError, DatagramEvent, Endpoint, SnapError, SnapSide, |
| 85 | }; |
| 86 | |
| 87 | mod error; |
| 88 | pub use crate::error::Error; |
| 89 | |
| 90 | mod packet; |
| 91 | |
| 92 | mod shared; |
| 93 | pub use crate::shared::{AssociationEvent, AssociationId, EcnCodepoint, EndpointEvent}; |
| 94 | |
| 95 | pub(crate) mod param; |
| 96 | |
| 97 | pub(crate) mod queue; |
| 98 | pub use crate::queue::reassembly_queue::{Chunk, Chunks}; |
| 99 | |
| 100 | pub(crate) mod util; |
| 101 | |
| 102 | /// Fuzz helpers. Not part of the public API. |
| 103 | #[cfg(feature = "_fuzz")] |
| 104 | pub 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)] |
| 135 | pub enum Side { |
| 136 | /// The initiator of an association |
| 137 | #[default] |
| 138 | Client = 0, |
| 139 | /// The acceptor of an association |
| 140 | Server = 1, |
| 141 | } |
| 142 | |
| 143 | impl 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 | |
| 153 | impl 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 | |
| 167 | impl 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 | |
| 177 | use crate::packet::PartialDecode; |
| 178 | |
| 179 | /// Payload in Incoming/outgoing Transmit |
| 180 | #[derive(Debug)] |
| 181 | pub enum Payload { |
| 182 | PartialDecode(PartialDecode), |
| 183 | RawEncode(Vec<Bytes>), |
| 184 | } |
| 185 | |
| 186 | /// Incoming/outgoing Transmit |
| 187 | #[derive(Debug)] |
| 188 | pub 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)] |
| 202 | mod 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 | } |