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