Skip to main content

rice_c/
lib.rs

1// Copyright (C) 2025 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11#![deny(missing_debug_implementations)]
12#![deny(missing_docs)]
13#![cfg_attr(docsrs, feature(doc_cfg))]
14
15//! # rice-c
16//!
17//! Bindings for the [rice-proto] C API.
18//!
19//! ## When to use
20//!
21//! The `rice-c` crate is useful when you have two separate components written in different
22//! languages that need to access and modify the same ICE resources. If your application stack is
23//! entirely in rust, then using only [rice-proto] may be sufficient and `rice-c` may not be needed.
24//!
25//! ## Building
26//!
27//! `rice-c` requires a pre-existing installation of the [rice-proto] C API that can be found using
28//! `pkg-config`. This detection is performed using [system-deps] and there are some environment
29//! variables that [system-deps] can use to influence the detection of a [rice-proto]
30//! installation.
31//!
32//! You can check if [rice-proto] is available in your build environment with:
33//!
34//! ```sh
35//! pkg-config --modversion rice-proto
36//! ```
37//!
38//! ## Interface
39//!
40//! `rice-c` provides a very similar interface as [rice-proto] in order to ease switching between
41//! the two implementations (`rice-c` and [rice-proto]) as may be required.
42//!
43//! [rice-proto]: https://docs.rs/rice-proto
44//! [system-deps]: https://docs.rs/system-deps
45
46use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
47
48pub mod ffi;
49
50pub mod agent;
51pub mod candidate;
52pub mod component;
53pub mod stream;
54pub mod turn;
55
56pub use sans_io_time::Instant;
57
58/// Prelude module.
59pub mod prelude {
60    pub use crate::candidate::CandidateApi;
61}
62
63/// A network address.
64pub struct Address {
65    ffi: *mut crate::ffi::RiceAddress,
66}
67
68unsafe impl Send for Address {}
69unsafe impl Sync for Address {}
70
71impl core::fmt::Debug for Address {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        if self.ffi.is_null() {
74            f.debug_struct("Address").field("ffi", &self.ffi).finish()
75        } else {
76            f.debug_struct("Address")
77                .field("ffi", &self.ffi)
78                .field("value", &self.as_socket())
79                .finish()
80        }
81    }
82}
83
84impl Clone for Address {
85    fn clone(&self) -> Self {
86        Self {
87            ffi: unsafe { crate::ffi::rice_address_copy(self.ffi) },
88        }
89    }
90}
91
92impl Drop for Address {
93    fn drop(&mut self) {
94        unsafe { crate::ffi::rice_address_free(self.ffi) }
95    }
96}
97
98impl Address {
99    pub(crate) fn as_c(&self) -> *mut crate::ffi::RiceAddress {
100        self.ffi
101    }
102
103    pub(crate) fn into_c_full(self) -> *mut crate::ffi::RiceAddress {
104        let ret = self.ffi;
105        core::mem::forget(self);
106        ret
107    }
108
109    pub(crate) fn from_c_none(ffi: *const crate::ffi::RiceAddress) -> Self {
110        Self {
111            ffi: unsafe { crate::ffi::rice_address_copy(ffi) },
112        }
113    }
114
115    pub(crate) fn from_c_full(ffi: *mut crate::ffi::RiceAddress) -> Self {
116        Self { ffi }
117    }
118
119    /// Convert this [`Address`] into a `SocketAddr`.
120    pub fn as_socket(&self) -> SocketAddr {
121        self.into()
122    }
123}
124
125impl From<SocketAddr> for Address {
126    fn from(addr: SocketAddr) -> Self {
127        match addr.ip() {
128            IpAddr::V4(v4) => Self {
129                ffi: unsafe {
130                    crate::ffi::rice_address_new_from_bytes(
131                        crate::ffi::RICE_ADDRESS_FAMILY_IPV4,
132                        v4.octets().as_ptr(),
133                        addr.port(),
134                    )
135                },
136            },
137            IpAddr::V6(v6) => Self {
138                ffi: unsafe {
139                    crate::ffi::rice_address_new_from_bytes(
140                        crate::ffi::RICE_ADDRESS_FAMILY_IPV6,
141                        v6.octets().as_ptr(),
142                        addr.port(),
143                    )
144                },
145            },
146        }
147    }
148}
149
150impl From<&Address> for SocketAddr {
151    fn from(value: &Address) -> Self {
152        unsafe {
153            let port = crate::ffi::rice_address_get_port(value.ffi);
154            let ip = match crate::ffi::rice_address_get_family(value.ffi) {
155                crate::ffi::RICE_ADDRESS_FAMILY_IPV4 => {
156                    let mut octets = [0; 4];
157                    crate::ffi::rice_address_get_address_bytes(value.ffi, octets.as_mut_ptr());
158                    IpAddr::V4(Ipv4Addr::from(octets))
159                }
160                crate::ffi::RICE_ADDRESS_FAMILY_IPV6 => {
161                    let mut octets = [0; 16];
162                    crate::ffi::rice_address_get_address_bytes(value.ffi, octets.as_mut_ptr());
163                    IpAddr::V6(Ipv6Addr::from(octets))
164                }
165                val => panic!("Unknown address family value {val:x?}"),
166            };
167            SocketAddr::new(ip, port)
168        }
169    }
170}
171
172impl std::str::FromStr for Address {
173    type Err = std::net::AddrParseError;
174
175    fn from_str(s: &str) -> Result<Self, Self::Err> {
176        let addr: SocketAddr = s.parse()?;
177        Ok(Self::from(addr))
178    }
179}
180
181impl PartialEq<Address> for Address {
182    fn eq(&self, other: &Address) -> bool {
183        unsafe { crate::ffi::rice_address_cmp(self.ffi, other.ffi) == 0 }
184    }
185}
186
187/// The family of an address.
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189#[repr(u32)]
190pub enum AddressFamily {
191    /// Version 4 of the Internet Protocol.
192    IPV4 = crate::ffi::RICE_ADDRESS_FAMILY_IPV4,
193    /// Version 6 of the Internet Protocol.
194    IPV6 = crate::ffi::RICE_ADDRESS_FAMILY_IPV6,
195}
196
197impl From<crate::ffi::RiceAddressFamily> for AddressFamily {
198    fn from(value: crate::ffi::RiceAddressFamily) -> Self {
199        match value {
200            crate::ffi::RICE_ADDRESS_FAMILY_IPV4 => Self::IPV4,
201            crate::ffi::RICE_ADDRESS_FAMILY_IPV6 => Self::IPV6,
202            val => panic!("Unknown address family value {val:x?}"),
203        }
204    }
205}
206
207impl From<AddressFamily> for crate::ffi::RiceAddressFamily {
208    fn from(value: AddressFamily) -> Self {
209        match value {
210            AddressFamily::IPV4 => crate::ffi::RICE_ADDRESS_FAMILY_IPV4,
211            AddressFamily::IPV6 => crate::ffi::RICE_ADDRESS_FAMILY_IPV6,
212        }
213    }
214}
215
216/// The supported authentication mechanisms.
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218#[repr(u32)]
219pub enum IntegrityAlgorithm {
220    /// The SHA-1 HMAC.
221    Sha1 = crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA1,
222    /// The SHA-256 HMAC.
223    Sha256 = crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA256,
224}
225
226impl From<crate::ffi::RiceIntegrityAlgorithm> for IntegrityAlgorithm {
227    fn from(value: crate::ffi::RiceIntegrityAlgorithm) -> Self {
228        match value {
229            crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA1 => Self::Sha1,
230            crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA256 => Self::Sha256,
231            val => panic!("Unknown integrity algorithm value {val:x?}"),
232        }
233    }
234}
235
236impl From<IntegrityAlgorithm> for crate::ffi::RiceIntegrityAlgorithm {
237    fn from(value: IntegrityAlgorithm) -> Self {
238        match value {
239            IntegrityAlgorithm::Sha1 => crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA1,
240            IntegrityAlgorithm::Sha256 => crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA256,
241        }
242    }
243}
244
245/// A feature.
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247#[repr(i32)]
248pub enum Feature {
249    /// The configuration will automatically be used when supported.
250    Disabled = crate::ffi::RICE_FEATURE_DISABLED,
251    /// The configuration will automatically be used when supported.
252    Auto = crate::ffi::RICE_FEATURE_AUTO,
253    /// The configuration is enabled and required.
254    Required = crate::ffi::RICE_FEATURE_REQUIRED,
255}
256
257impl From<crate::ffi::RiceFeature> for Feature {
258    fn from(value: crate::ffi::RiceFeature) -> Self {
259        match value {
260            crate::ffi::RICE_FEATURE_DISABLED => Self::Disabled,
261            crate::ffi::RICE_FEATURE_AUTO => Self::Auto,
262            crate::ffi::RICE_FEATURE_REQUIRED => Self::Required,
263            val => panic!("Unknown feature value {val:x?}"),
264        }
265    }
266}
267
268impl From<Feature> for crate::ffi::RiceFeature {
269    fn from(value: Feature) -> Self {
270        match value {
271            Feature::Disabled => crate::ffi::RICE_FEATURE_DISABLED,
272            Feature::Auto => crate::ffi::RICE_FEATURE_AUTO,
273            Feature::Required => crate::ffi::RICE_FEATURE_REQUIRED,
274        }
275    }
276}
277
278fn mut_override<T>(val: *const T) -> *mut T {
279    val as *mut T
280}
281
282fn const_override<T>(val: *mut T) -> *const T {
283    val as *const T
284}
285
286/// Generate a random sequence of characters suitable for username fragments and passwords.
287pub fn random_string(len: usize) -> String {
288    if len == 0 {
289        return String::new();
290    }
291    unsafe {
292        let ptr = crate::ffi::rice_random_string(len);
293        let s = core::ffi::CStr::from_ptr(ptr).to_str().unwrap();
294        let ret = s.to_string();
295        crate::ffi::rice_string_free(ptr);
296        ret
297    }
298}
299
300#[cfg(test)]
301pub(crate) mod tests {
302    use tracing::subscriber::DefaultGuard;
303    use tracing_subscriber::Layer;
304    use tracing_subscriber::layer::SubscriberExt;
305
306    use super::*;
307
308    pub fn test_init_log() -> DefaultGuard {
309        let level_filter = std::env::var("RICE_LOG")
310            .or(std::env::var("RUST_LOG"))
311            .ok()
312            .and_then(|var| var.parse::<tracing_subscriber::filter::Targets>().ok())
313            .unwrap_or(
314                tracing_subscriber::filter::Targets::new().with_default(tracing::Level::TRACE),
315            );
316        let registry = tracing_subscriber::registry().with(
317            tracing_subscriber::fmt::layer()
318                .with_file(true)
319                .with_line_number(true)
320                .with_level(true)
321                .with_target(false)
322                .with_test_writer()
323                .with_filter(level_filter),
324        );
325        tracing::subscriber::set_default(registry)
326    }
327
328    #[test]
329    fn random_string() {
330        assert!(crate::random_string(0).is_empty());
331        assert_eq!(crate::random_string(4).len(), 4);
332        println!("{}", crate::random_string(128));
333    }
334
335    #[test]
336    fn enums() {
337        let _log = test_init_log();
338
339        for (c, r) in [
340            (crate::ffi::RICE_ADDRESS_FAMILY_IPV4, AddressFamily::IPV4),
341            (crate::ffi::RICE_ADDRESS_FAMILY_IPV6, AddressFamily::IPV6),
342        ] {
343            assert_eq!(AddressFamily::from(c), r);
344            assert_eq!(crate::ffi::RiceAddressFamily::from(r), c);
345        }
346
347        for (c, r) in [
348            (
349                crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA1,
350                IntegrityAlgorithm::Sha1,
351            ),
352            (
353                crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA256,
354                IntegrityAlgorithm::Sha256,
355            ),
356        ] {
357            assert_eq!(IntegrityAlgorithm::from(c), r);
358            assert_eq!(crate::ffi::RiceIntegrityAlgorithm::from(r), c);
359        }
360
361        for (c, r) in [
362            (
363                crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA1,
364                IntegrityAlgorithm::Sha1,
365            ),
366            (
367                crate::ffi::RICE_INTEGRITY_ALGORITHM_SHA256,
368                IntegrityAlgorithm::Sha256,
369            ),
370        ] {
371            assert_eq!(IntegrityAlgorithm::from(c), r);
372            assert_eq!(crate::ffi::RiceIntegrityAlgorithm::from(r), c);
373        }
374
375        for (c, r) in [
376            (crate::ffi::RICE_FEATURE_DISABLED, Feature::Disabled),
377            (crate::ffi::RICE_FEATURE_AUTO, Feature::Auto),
378            (crate::ffi::RICE_FEATURE_REQUIRED, Feature::Required),
379        ] {
380            assert_eq!(Feature::from(c), r);
381            assert_eq!(crate::ffi::RiceFeature::from(r), c);
382        }
383    }
384
385    #[test]
386    #[should_panic = "Unknown feature value"]
387    fn feature_out_of_range() {
388        let _ = Feature::from(i32::MAX);
389    }
390
391    #[test]
392    #[should_panic = "Unknown address family value"]
393    fn address_family_out_of_range() {
394        let _ = AddressFamily::from(u32::MAX);
395    }
396
397    #[test]
398    #[should_panic = "Unknown integrity algorithm value"]
399    fn integrity_algorithm_out_of_range() {
400        let _ = IntegrityAlgorithm::from(u32::MAX);
401    }
402}