rice_c/
agent.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//! ICE Agent implementation as specified in RFC 8445
10
11use crate::{mut_override, stream::Stream};
12
13pub use crate::stream::Credentials as TurnCredentials;
14
15/// An ICE agent as specified in RFC 8445
16#[derive(Debug)]
17pub struct Agent {
18    ffi: *mut crate::ffi::RiceAgent,
19}
20
21unsafe impl Send for Agent {}
22unsafe impl Sync for Agent {}
23
24impl Clone for Agent {
25    fn clone(&self) -> Self {
26        Self {
27            ffi: unsafe { crate::ffi::rice_agent_ref(self.ffi) },
28        }
29    }
30}
31
32impl Drop for Agent {
33    fn drop(&mut self) {
34        unsafe { crate::ffi::rice_agent_unref(self.ffi) }
35    }
36}
37
38impl Default for Agent {
39    fn default() -> Self {
40        Agent::builder().build()
41    }
42}
43
44impl Agent {
45    /// Create a new [`AgentBuilder`]
46    pub fn builder() -> AgentBuilder {
47        AgentBuilder::default()
48    }
49
50    /// A process-unique identifier for this agent.
51    pub fn id(&self) -> u64 {
52        unsafe { crate::ffi::rice_agent_id(self.ffi) }
53    }
54
55    /// The current time in microseconds.
56    pub fn now(&self) -> u64 {
57        unsafe { crate::ffi::rice_agent_now(self.ffi) }
58    }
59
60    /// Add a new `Stream` to this agent
61    ///
62    /// # Examples
63    ///
64    /// Add a `Stream`
65    ///
66    /// ```
67    /// # use rice_c::agent::Agent;
68    /// let agent = Agent::default();
69    /// let s = agent.add_stream();
70    /// ```
71    pub fn add_stream(&self) -> crate::stream::Stream {
72        unsafe { Stream::from_c_full(crate::ffi::rice_agent_add_stream(self.ffi)) }
73    }
74
75    /// Retrieve a [`Stream`] by its ID from this [`Agent`].
76    pub fn stream(&self, id: usize) -> Option<crate::stream::Stream> {
77        let ret = unsafe { crate::ffi::rice_agent_get_stream(self.ffi, id) };
78        if ret.is_null() {
79            None
80        } else {
81            Some(crate::stream::Stream::from_c_full(ret))
82        }
83    }
84
85    /// Close the agent loop.  Applications should wait for [`Agent::poll`] to return
86    /// [`AgentPoll::Closed`] after calling this function.
87    pub fn close(&self, now_micros: u64) {
88        unsafe { crate::ffi::rice_agent_close(self.ffi, now_micros) }
89    }
90
91    /// The controlling state of this ICE agent.  This value may change throughout the ICE
92    /// negotiation process.
93    pub fn controlling(&self) -> bool {
94        unsafe { crate::ffi::rice_agent_get_controlling(self.ffi) }
95    }
96
97    /// Add a STUN server by address and transport to use for gathering potential candidates
98    pub fn add_stun_server(
99        &self,
100        transport: crate::candidate::TransportType,
101        addr: crate::Address,
102    ) {
103        unsafe { crate::ffi::rice_agent_add_stun_server(self.ffi, transport.into(), addr.as_c()) }
104    }
105
106    /// Add a STUN server by address and transport to use for gathering potential candidates
107    pub fn add_turn_server(
108        &self,
109        transport: crate::candidate::TransportType,
110        addr: crate::Address,
111        credentials: TurnCredentials,
112    ) {
113        unsafe {
114            crate::ffi::rice_agent_add_turn_server(
115                self.ffi,
116                transport.into(),
117                addr.as_c(),
118                credentials.into_c_none(),
119            )
120        }
121    }
122
123    /// Poll the [`Agent`] for further progress to be made.
124    ///
125    /// The returned value indicates what the application needs to do.
126    pub fn poll(&self, now_micros: u64) -> AgentPoll {
127        let mut ret = crate::ffi::RiceAgentPoll {
128            tag: crate::ffi::RICE_AGENT_POLL_CLOSED,
129            field1: crate::ffi::RiceAgentPoll__bindgen_ty_1 {
130                field1: core::mem::ManuallyDrop::new(
131                    crate::ffi::RiceAgentPoll__bindgen_ty_1__bindgen_ty_1 {
132                        wait_until_micros: 0,
133                    },
134                ),
135            },
136        };
137
138        unsafe {
139            crate::ffi::rice_agent_poll_init(&mut ret);
140            crate::ffi::rice_agent_poll(self.ffi, now_micros, &mut ret);
141        }
142
143        AgentPoll::from_c_full(ret)
144    }
145
146    /// Poll for a transmission to be performed.
147    ///
148    /// If not-None, then the provided data must be sent to the peer from the provided socket
149    /// address.
150    pub fn poll_transmit(&self, now_micros: u64) -> Option<AgentTransmit> {
151        let mut ret = crate::ffi::RiceTransmit {
152            stream_id: 0,
153            transport: crate::ffi::RICE_TRANSPORT_TYPE_UDP,
154            from: core::ptr::null(),
155            to: core::ptr::null(),
156            data: crate::ffi::RiceDataImpl {
157                ptr: core::ptr::null_mut(),
158                size: 0,
159            },
160        };
161        unsafe { crate::ffi::rice_agent_poll_transmit(self.ffi, now_micros, &mut ret) }
162        if ret.from.is_null() || ret.to.is_null() {
163            return None;
164        }
165        Some(AgentTransmit::from_c_full(ret))
166    }
167    // TODO: stun_servers(), add_turn_server(), turn_servers(), stream()
168}
169
170/// A builder for an [`Agent`]
171#[derive(Debug, Default)]
172pub struct AgentBuilder {
173    trickle_ice: bool,
174    controlling: bool,
175}
176
177impl AgentBuilder {
178    /// Whether candidates can trickle in during ICE negotiation
179    pub fn trickle_ice(mut self, trickle_ice: bool) -> Self {
180        self.trickle_ice = trickle_ice;
181        self
182    }
183
184    /// The initial value of the controlling attribute.  During the ICE negotiation, the
185    /// controlling value may change.
186    pub fn controlling(mut self, controlling: bool) -> Self {
187        self.controlling = controlling;
188        self
189    }
190
191    /// Construct a new [`Agent`]
192    pub fn build(self) -> Agent {
193        Agent {
194            ffi: unsafe { crate::ffi::rice_agent_new(self.controlling, self.trickle_ice) },
195        }
196    }
197}
198
199/// Indicates what the caller should do after calling [`Agent::poll`]
200#[derive(Debug, Default)]
201pub enum AgentPoll {
202    /// The Agent is closed.  No further progress will be made.
203    #[default]
204    Closed,
205    /// Wait until the specified `Instant` has been reached (or an external event)
206    WaitUntilMicros(u64),
207    /// Connect from the specified interface to the specified address.  Reply (success or failure)
208    /// should be notified using [`Stream::allocated_socket`] with the same parameters.
209    AllocateSocket(AgentSocket),
210    /// It is posible to remove the specified 5-tuple. The socket will not be referenced any
211    /// further.
212    RemoveSocket(AgentSocket),
213    /// A new pair has been selected for a component.
214    SelectedPair(AgentSelectedPair),
215    /// A [`Component`](crate::component::Component) has changed state.
216    ComponentStateChange(AgentComponentStateChange),
217    /// A [`Component`](crate::component::Component) has gathered a candidate.
218    GatheredCandidate(AgentGatheredCandidate),
219    /// A [`Component`](crate::component::Component) has completed gathering.
220    GatheringComplete(AgentGatheringComplete),
221}
222
223impl AgentPoll {
224    fn from_c_full(mut ffi: crate::ffi::RiceAgentPoll) -> Self {
225        unsafe {
226            let ret = match ffi.tag {
227                crate::ffi::RICE_AGENT_POLL_CLOSED => Self::Closed,
228                crate::ffi::RICE_AGENT_POLL_WAIT_UNTIL_MICROS => Self::WaitUntilMicros(
229                    core::mem::ManuallyDrop::into_inner(ffi.field1.field1).wait_until_micros,
230                ),
231                crate::ffi::RICE_AGENT_POLL_ALLOCATE_SOCKET => {
232                    let ty = core::mem::ManuallyDrop::into_inner(ffi.field1.field2).allocate_socket;
233                    Self::AllocateSocket(AgentSocket {
234                        stream_id: ty.stream_id,
235                        component_id: ty.component_id,
236                        transport: ty.transport.into(),
237                        from: crate::Address::from_c_full(mut_override(ty.from)),
238                        to: crate::Address::from_c_full(mut_override(ty.to)),
239                    })
240                }
241                crate::ffi::RICE_AGENT_POLL_REMOVE_SOCKET => {
242                    let ty = core::mem::ManuallyDrop::into_inner(ffi.field1.field3).remove_socket;
243                    ffi.tag = crate::ffi::RICE_AGENT_POLL_CLOSED;
244                    Self::RemoveSocket(AgentSocket {
245                        stream_id: ty.stream_id,
246                        component_id: ty.component_id,
247                        transport: ty.transport.into(),
248                        from: crate::Address::from_c_full(mut_override(ty.from)),
249                        to: crate::Address::from_c_full(mut_override(ty.to)),
250                    })
251                }
252                crate::ffi::RICE_AGENT_POLL_SELECTED_PAIR => {
253                    let mut ty =
254                        core::mem::ManuallyDrop::into_inner(ffi.field1.field4).selected_pair;
255                    let local = crate::candidate::Candidate::from_c_none(&ty.local);
256                    let remote = crate::candidate::Candidate::from_c_none(&ty.remote);
257                    crate::ffi::rice_candidate_clear(&mut ty.local);
258                    crate::ffi::rice_candidate_clear(&mut ty.remote);
259                    ffi.tag = crate::ffi::RICE_AGENT_POLL_CLOSED;
260                    Self::SelectedPair(AgentSelectedPair {
261                        stream_id: ty.stream_id,
262                        component_id: ty.component_id,
263                        local,
264                        remote,
265                    })
266                }
267                crate::ffi::RICE_AGENT_POLL_COMPONENT_STATE_CHANGE => {
268                    let ty = core::mem::ManuallyDrop::into_inner(ffi.field1.field5)
269                        .component_state_change;
270                    Self::ComponentStateChange(AgentComponentStateChange {
271                        stream_id: ty.stream_id,
272                        component_id: ty.component_id,
273                        state: crate::component::ComponentConnectionState::from_c(ty.state),
274                    })
275                }
276                crate::ffi::RICE_AGENT_POLL_GATHERED_CANDIDATE => {
277                    let ty =
278                        core::mem::ManuallyDrop::into_inner(ffi.field1.field6).gathered_candidate;
279                    let stream_id = ty.stream_id;
280                    let gathered = crate::stream::GatheredCandidate::from_c_full(ty.gathered);
281                    ffi.tag = crate::ffi::RICE_AGENT_POLL_CLOSED;
282                    Self::GatheredCandidate(AgentGatheredCandidate {
283                        stream_id,
284                        gathered,
285                    })
286                }
287                crate::ffi::RICE_AGENT_POLL_GATHERING_COMPLETE => {
288                    let ty =
289                        core::mem::ManuallyDrop::into_inner(ffi.field1.field7).gathering_complete;
290                    Self::GatheringComplete(AgentGatheringComplete {
291                        stream_id: ty.stream_id,
292                        component_id: ty.component_id,
293                    })
294                }
295                tag => panic!("Unkown AgentPoll value {tag:x?}"),
296            };
297            ret
298        }
299    }
300}
301
302impl Drop for AgentPoll {
303    fn drop(&mut self) {
304        unsafe {
305            if let Self::GatheredCandidate(gathered) = self {
306                let mut ret = crate::ffi::RiceAgentPoll {
307                    tag: crate::ffi::RICE_AGENT_POLL_GATHERED_CANDIDATE,
308                    field1: crate::ffi::RiceAgentPoll__bindgen_ty_1 {
309                        field6: core::mem::ManuallyDrop::new(
310                            crate::ffi::RiceAgentPoll__bindgen_ty_1__bindgen_ty_6 {
311                                gathered_candidate: crate::ffi::RiceAgentGatheredCandidate {
312                                    stream_id: gathered.stream_id,
313                                    gathered: crate::stream::GatheredCandidate::take(
314                                        &mut gathered.gathered,
315                                    )
316                                    .ffi,
317                                },
318                            },
319                        ),
320                    },
321                };
322                crate::ffi::rice_agent_poll_clear(&raw mut ret);
323            }
324        }
325    }
326}
327
328/// Transmit the data using the specified 5-tuple.
329#[derive(Debug)]
330pub struct AgentTransmit {
331    /// The ICE stream id.
332    pub stream_id: usize,
333    /// The socket to send the data from.
334    pub from: crate::Address,
335    /// The network address to send the data to.
336    pub to: crate::Address,
337    /// The transport to send the data over.
338    pub transport: crate::candidate::TransportType,
339    /// The data to send.
340    pub data: &'static [u8],
341}
342
343impl AgentTransmit {
344    pub(crate) fn from_c_full(ffi: crate::ffi::RiceTransmit) -> Self {
345        unsafe {
346            let data = ffi.data.ptr;
347            let len = ffi.data.size;
348            let data = core::slice::from_raw_parts(data, len);
349            AgentTransmit {
350                stream_id: ffi.stream_id,
351                from: crate::Address::from_c_full(mut_override(ffi.from)),
352                to: crate::Address::from_c_full(mut_override(ffi.to)),
353                transport: ffi.transport.into(),
354                data,
355            }
356        }
357    }
358}
359
360impl Drop for AgentTransmit {
361    fn drop(&mut self) {
362        unsafe {
363            let mut transmit = crate::ffi::RiceTransmit {
364                stream_id: self.stream_id,
365                from: core::ptr::null_mut(),
366                to: core::ptr::null_mut(),
367                transport: self.transport.into(),
368                data: crate::ffi::RiceDataImpl::to_c(self.data),
369            };
370            crate::ffi::rice_transmit_clear(&mut transmit);
371        }
372    }
373}
374
375/// A socket with the specified network 5-tuple.
376#[derive(Debug)]
377pub struct AgentSocket {
378    /// The ICE stream id.
379    pub stream_id: usize,
380    /// The ICE component id.
381    pub component_id: usize,
382    /// The transport.
383    pub transport: crate::candidate::TransportType,
384    /// The socket source address.
385    pub from: crate::Address,
386    /// The socket destination address.
387    pub to: crate::Address,
388}
389
390/// A new pair has been selected for a component.
391#[derive(Debug)]
392pub struct AgentSelectedPair {
393    pub stream_id: usize,
394    pub component_id: usize,
395    pub local: crate::candidate::Candidate,
396    pub remote: crate::candidate::Candidate,
397}
398
399/// A [`Component`](crate::component::Component) has changed state.
400#[derive(Debug)]
401#[repr(C)]
402pub struct AgentComponentStateChange {
403    /// The ICE stream id.
404    pub stream_id: usize,
405    /// The ICE component id.
406    pub component_id: usize,
407    /// The new state of the component.
408    pub state: crate::component::ComponentConnectionState,
409}
410
411/// A [`Component`](crate::component::Component) has gathered a candidate.
412#[derive(Debug)]
413#[repr(C)]
414pub struct AgentGatheredCandidate {
415    /// The ICE stream id.
416    pub stream_id: usize,
417    /// The gathered candidate.
418    pub gathered: crate::stream::GatheredCandidate,
419}
420
421/// A [`Component`](crate::component::Component) has completed gathering.
422#[derive(Debug)]
423#[repr(C)]
424pub struct AgentGatheringComplete {
425    /// The ICE stream id.
426    pub stream_id: usize,
427    /// The ICE component id.
428    pub component_id: usize,
429}
430
431/// Errors that can be returned as a result of agent operations.
432#[derive(Debug)]
433#[repr(i32)]
434pub enum AgentError {
435    Failed = crate::ffi::RICE_ERROR_FAILED,
436    ResourceNotFound = crate::ffi::RICE_ERROR_RESOURCE_NOT_FOUND,
437    AlreadyInProgress = crate::ffi::RICE_ERROR_ALREADY_IN_PROGRESS,
438}
439
440impl core::fmt::Display for AgentError {
441    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442        match self {
443            Self::Failed => write!(f, "Failed"),
444            Self::ResourceNotFound => write!(f, "Resource Not Found"),
445            Self::AlreadyInProgress => write!(f, "Already In Progress"),
446        }
447    }
448}
449
450impl AgentError {
451    pub(crate) fn from_c(value: crate::ffi::RiceError) -> Result<(), AgentError> {
452        match value {
453            crate::ffi::RICE_ERROR_SUCCESS => Ok(()),
454            crate::ffi::RICE_ERROR_FAILED => Err(AgentError::Failed),
455            crate::ffi::RICE_ERROR_RESOURCE_NOT_FOUND => Err(AgentError::ResourceNotFound),
456            crate::ffi::RICE_ERROR_ALREADY_IN_PROGRESS => Err(AgentError::AlreadyInProgress),
457            val => panic!("unknown RiceError value {val:x?}"),
458        }
459    }
460}