libjuice_rs/
server.rs

1//! Embedded TURN server.
2use std::ffi::CString;
3use std::marker::{PhantomData, PhantomPinned};
4use std::net::{IpAddr, SocketAddr};
5use std::ptr;
6
7use libjuice_sys as sys;
8
9use crate::log::ensure_logging;
10use crate::{Error, Result};
11
12pub struct Credentials {
13    username: CString,
14    password: CString,
15    quota: Option<i32>,
16}
17
18impl Credentials {
19    pub fn new<T: Into<Vec<u8>>>(username: T, password: T, quota: Option<i32>) -> Result<Self> {
20        Ok(Self {
21            username: CString::new(username).map_err(|_| Error::InvalidArgument)?,
22            password: CString::new(password).map_err(|_| Error::InvalidArgument)?,
23            quota,
24        })
25    }
26}
27
28/// TURN server builder.
29#[derive(Default)]
30pub struct Builder {
31    credentials: Vec<Credentials>,
32    bind_address: Option<CString>,
33    external_address: Option<CString>,
34    port: u16,
35    max_allocations: i32,
36    max_peers: i32,
37    relay_port_range: Option<(u16, u16)>,
38    realm: Option<CString>,
39}
40
41/// TURN server.
42pub struct Server {
43    server: *mut sys::juice_server_t,
44    _marker: PhantomData<(sys::juice_server, PhantomPinned)>,
45}
46
47impl Builder {
48    /// Build [`Server`].
49    pub fn build(self) -> Result<Server> {
50        ensure_logging();
51
52        let mut credentials = self
53            .credentials
54            .iter()
55            .map(|cred| sys::juice_server_credentials {
56                username: cred.username.as_ptr(),
57                password: cred.password.as_ptr(),
58                allocations_quota: cred.quota.unwrap_or_default(),
59            })
60            .collect::<Vec<_>>();
61
62        let credentials = if credentials.is_empty() {
63            return Err(Error::InvalidArgument);
64        } else {
65            credentials.as_mut_ptr()
66        };
67
68        let port_range = self.relay_port_range.unwrap_or_default();
69
70        let bind_address = self
71            .bind_address
72            .as_ref()
73            .map(|v| v.as_ptr())
74            .unwrap_or(ptr::null());
75
76        let external_address = self
77            .external_address
78            .as_ref()
79            .map(|v| v.as_ptr())
80            .unwrap_or(ptr::null());
81
82        let realm = self
83            .realm
84            .as_ref()
85            .map(|v| v.as_ptr())
86            .unwrap_or(ptr::null());
87
88        let config = sys::juice_server_config {
89            credentials,
90            credentials_count: self.credentials.len() as _,
91            bind_address,
92            external_address,
93            max_allocations: self.max_allocations,
94            max_peers: self.max_peers,
95            port: self.port,
96            relay_port_range_begin: port_range.0,
97            relay_port_range_end: port_range.1,
98            realm,
99        };
100
101        // finally try to build
102        let ptr = unsafe { sys::juice_server_create(&config as _) };
103
104        if ptr.is_null() {
105            Err(Error::Failed)
106        } else {
107            Ok(Server {
108                server: ptr,
109                _marker: Default::default(),
110            })
111        }
112    }
113
114    /// Set several credentials at once.
115    ///
116    /// This function will overwrite credentials list entirely. Alternatively, you can
117    /// sequentially call [`Builder::add_credentials`]
118    pub fn with_credentials<I: Iterator<Item = Credentials>>(
119        mut self,
120        credentials_list: I,
121    ) -> Self {
122        self.credentials = credentials_list.collect();
123        self
124    }
125
126    /// Append credentials to the list.
127    pub fn add_credentials(mut self, cred: Credentials) -> Self {
128        self.credentials.push(cred);
129        self
130    }
131
132    /// Bind to specific interface and port.
133    pub fn bind_address(mut self, addr: &SocketAddr) -> Self {
134        self.bind_address = Some(CString::new(addr.ip().to_string()).unwrap());
135        self.port = addr.port();
136        self
137    }
138
139    pub fn with_external_address(mut self, addr: &IpAddr) -> Self {
140        self.external_address = Some(CString::new(addr.to_string()).unwrap());
141        self
142    }
143
144    /// Set relayed port range.
145    pub fn with_port_range(mut self, begin: u16, end: u16) -> Self {
146        self.relay_port_range = Some((begin, end));
147        self
148    }
149
150    /// Set realm.
151    pub fn with_realm<T: Into<Vec<u8>>>(mut self, realm: T) -> Result<Self> {
152        self.realm = Some(CString::new(realm).map_err(|_| Error::InvalidArgument)?);
153        Ok(self)
154    }
155
156    pub fn with_allocations_limit(mut self, limit: u32) -> Self {
157        self.max_allocations = std::cmp::min(limit, i32::MAX as u32) as i32;
158        self
159    }
160
161    pub fn with_peers_limit(mut self, limit: u32) -> Self {
162        self.max_peers = std::cmp::min(limit, i32::MAX as u32) as i32;
163        self
164    }
165}
166
167unsafe impl Send for Server {}
168
169unsafe impl Sync for Server {}
170
171impl Server {
172    /// Create server builder
173    pub fn builder() -> Builder {
174        Default::default()
175    }
176
177    /// Get listen port
178    pub fn get_port(&self) -> u16 {
179        unsafe { sys::juice_server_get_port(self.server) }
180    }
181}
182
183impl Drop for Server {
184    fn drop(&mut self) {
185        unsafe { sys::juice_server_destroy(self.server) }
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192
193    #[test]
194    fn build() {
195        crate::test_util::logger_init();
196        let creds = Credentials::new("a", "b", None).unwrap();
197
198        let _ = Server::builder()
199            .add_credentials(creds)
200            .build()
201            .ok()
202            .unwrap();
203    }
204}