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
14//! # rice-c
15//!
16//! Bindings for the [rice-proto] C API.
17//!
18//! ## When to use
19//!
20//! The `rice-c` crate is useful when you have two separate components written in different
21//! languages that need to access and modify the same ICE resources. If your application stack is
22//! entirely in rust, then using only [rice-proto] may be sufficient and `rice-c` may not be needed.
23//!
24//! ## Building
25//!
26//! `rice-c` requires a pre-existing installation of the [rice-proto] C API that can be found using
27//! `pkg-config`. This detection is performed using [system-deps] and there are some environment
28//! variables that [system-deps] can use to influence the detection of a [rice-proto]
29//! installation.
30//!
31//! You can check if [rice-proto] is available in your build environment with:
32//!
33//! ```sh
34//! pkg-config --modversion rice-proto
35//! ```
36//!
37//! ## Interface
38//!
39//! `rice-c` provides a very similar interface as [rice-proto] in order to ease switching between
40//! the two implementations (`rice-c` and [rice-proto]) as may be required.
41//!
42//! [rice-proto]: https://docs.rs/rice-proto
43//! [system-deps]: https://docs.rs/system-deps
44
45use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
46
47pub mod ffi;
48
49pub mod agent;
50pub mod candidate;
51pub mod component;
52pub mod stream;
53pub mod turn;
54
55pub use sans_io_time::Instant;
56
57/// Prelude module.
58pub mod prelude {
59    pub use crate::candidate::CandidateApi;
60}
61
62/// A network address.
63pub struct Address {
64    ffi: *mut crate::ffi::RiceAddress,
65}
66
67unsafe impl Send for Address {}
68unsafe impl Sync for Address {}
69
70impl core::fmt::Debug for Address {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        if self.ffi.is_null() {
73            f.debug_struct("Address").field("ffi", &self.ffi).finish()
74        } else {
75            f.debug_struct("Address")
76                .field("ffi", &self.ffi)
77                .field("value", &self.as_socket())
78                .finish()
79        }
80    }
81}
82
83impl Clone for Address {
84    fn clone(&self) -> Self {
85        Self {
86            ffi: unsafe { crate::ffi::rice_address_copy(self.ffi) },
87        }
88    }
89}
90
91impl Drop for Address {
92    fn drop(&mut self) {
93        unsafe { crate::ffi::rice_address_free(self.ffi) }
94    }
95}
96
97impl Address {
98    pub(crate) fn as_c(&self) -> *mut crate::ffi::RiceAddress {
99        self.ffi
100    }
101
102    pub(crate) fn into_c_full(self) -> *mut crate::ffi::RiceAddress {
103        let ret = self.ffi;
104        core::mem::forget(self);
105        ret
106    }
107
108    pub(crate) fn from_c_none(ffi: *const crate::ffi::RiceAddress) -> Self {
109        Self {
110            ffi: unsafe { crate::ffi::rice_address_copy(ffi) },
111        }
112    }
113
114    pub(crate) fn from_c_full(ffi: *mut crate::ffi::RiceAddress) -> Self {
115        Self { ffi }
116    }
117
118    /// Convert this [`Address`] into a `SocketAddr`.
119    pub fn as_socket(&self) -> SocketAddr {
120        self.into()
121    }
122}
123
124impl From<SocketAddr> for Address {
125    fn from(addr: SocketAddr) -> Self {
126        match addr.ip() {
127            IpAddr::V4(v4) => Self {
128                ffi: unsafe {
129                    crate::ffi::rice_address_new_from_bytes(
130                        crate::ffi::RICE_ADDRESS_FAMILY_IPV4,
131                        v4.octets().as_ptr(),
132                        addr.port(),
133                    )
134                },
135            },
136            IpAddr::V6(v6) => Self {
137                ffi: unsafe {
138                    crate::ffi::rice_address_new_from_bytes(
139                        crate::ffi::RICE_ADDRESS_FAMILY_IPV6,
140                        v6.octets().as_ptr(),
141                        addr.port(),
142                    )
143                },
144            },
145        }
146    }
147}
148
149impl From<&Address> for SocketAddr {
150    fn from(value: &Address) -> Self {
151        unsafe {
152            let port = crate::ffi::rice_address_get_port(value.ffi);
153            let ip = match crate::ffi::rice_address_get_family(value.ffi) {
154                crate::ffi::RICE_ADDRESS_FAMILY_IPV4 => {
155                    let mut octets = [0; 4];
156                    crate::ffi::rice_address_get_address_bytes(value.ffi, octets.as_mut_ptr());
157                    IpAddr::V4(Ipv4Addr::from(octets))
158                }
159                crate::ffi::RICE_ADDRESS_FAMILY_IPV6 => {
160                    let mut octets = [0; 16];
161                    crate::ffi::rice_address_get_address_bytes(value.ffi, octets.as_mut_ptr());
162                    IpAddr::V6(Ipv6Addr::from(octets))
163                }
164                val => panic!("Unknown address family value {val:x?}"),
165            };
166            SocketAddr::new(ip, port)
167        }
168    }
169}
170
171impl std::str::FromStr for Address {
172    type Err = std::net::AddrParseError;
173
174    fn from_str(s: &str) -> Result<Self, Self::Err> {
175        let addr: SocketAddr = s.parse()?;
176        Ok(Self::from(addr))
177    }
178}
179
180impl PartialEq<Address> for Address {
181    fn eq(&self, other: &Address) -> bool {
182        unsafe { crate::ffi::rice_address_cmp(self.ffi, other.ffi) == 0 }
183    }
184}
185
186/// The family of an address.
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188#[repr(u32)]
189pub enum AddressFamily {
190    /// Version 4 of the Internet Protocol.
191    IPV4 = crate::ffi::RICE_ADDRESS_FAMILY_IPV4,
192    /// Version 6 of the Internet Protocol.
193    IPV6 = crate::ffi::RICE_ADDRESS_FAMILY_IPV6,
194}
195
196fn mut_override<T>(val: *const T) -> *mut T {
197    val as *mut T
198}
199
200fn const_override<T>(val: *mut T) -> *const T {
201    val as *const T
202}
203
204/// Generate a random sequence of characters suitable for username fragments and passwords.
205pub fn random_string(len: usize) -> String {
206    if len == 0 {
207        return String::new();
208    }
209    unsafe {
210        let ptr = crate::ffi::rice_random_string(len);
211        let s = core::ffi::CStr::from_ptr(ptr).to_str().unwrap();
212        let ret = s.to_string();
213        crate::ffi::rice_string_free(ptr);
214        ret
215    }
216}
217
218#[cfg(test)]
219pub(crate) mod tests {
220    use tracing::subscriber::DefaultGuard;
221    use tracing_subscriber::Layer;
222    use tracing_subscriber::layer::SubscriberExt;
223
224    pub fn test_init_log() -> DefaultGuard {
225        let level_filter = std::env::var("RICE_LOG")
226            .or(std::env::var("RUST_LOG"))
227            .ok()
228            .and_then(|var| var.parse::<tracing_subscriber::filter::Targets>().ok())
229            .unwrap_or(
230                tracing_subscriber::filter::Targets::new().with_default(tracing::Level::TRACE),
231            );
232        let registry = tracing_subscriber::registry().with(
233            tracing_subscriber::fmt::layer()
234                .with_file(true)
235                .with_line_number(true)
236                .with_level(true)
237                .with_target(false)
238                .with_test_writer()
239                .with_filter(level_filter),
240        );
241        tracing::subscriber::set_default(registry)
242    }
243
244    #[test]
245    fn random_string() {
246        assert!(crate::random_string(0).is_empty());
247        assert_eq!(crate::random_string(4).len(), 4);
248        println!("{}", crate::random_string(128));
249    }
250}