Skip to main content

turn_server/
handler.rs

1#[cfg(feature = "api")]
2use std::sync::Arc;
3
4use crate::{
5    codec::{
6        crypto::{Password, generate_password, static_auth_secret},
7        message::attributes::PasswordAlgorithm,
8    },
9    config::Config,
10    service::{ServiceHandler, session::Identifier},
11    statistics::Statistics,
12};
13
14#[cfg(feature = "api")]
15use crate::api::{HooksEvent, RpcHooksService};
16
17use anyhow::Result;
18
19#[cfg(feature = "api")]
20use sdk::protos::{
21    TurnAllocatedEvent, TurnChannelBindEvent, TurnCreatePermissionEvent, TurnDestroyEvent,
22    TurnRefreshEvent,
23};
24
25#[cfg(feature = "api")]
26impl Into<sdk::protos::Identifier> for &Identifier {
27    fn into(self) -> sdk::protos::Identifier {
28        use crate::service::Transport;
29
30        sdk::protos::Identifier {
31            source: self.source.to_string(),
32            external: self.external.to_string(),
33            interface: self.interface.to_string(),
34            transport: match self.transport {
35                Transport::Udp => sdk::protos::Transport::Udp,
36                Transport::Tcp => sdk::protos::Transport::Tcp,
37            }
38            .into(),
39        }
40    }
41}
42
43#[derive(Clone)]
44pub struct Handler {
45    config: Config,
46    #[cfg(feature = "api")]
47    statistics: Statistics,
48    #[cfg(feature = "api")]
49    rpc: Arc<RpcHooksService>,
50}
51
52impl Handler {
53    #[allow(unused_variables)]
54    pub async fn new(config: Config, statistics: Statistics) -> Result<Self> {
55        Ok(Self {
56            #[cfg(feature = "api")]
57            rpc: RpcHooksService::new(&config).await?.into(),
58            #[cfg(feature = "api")]
59            statistics,
60            config,
61        })
62    }
63}
64
65impl ServiceHandler for Handler {
66    async fn get_password(
67        &self,
68        id: &Identifier,
69        username: &str,
70        algorithm: PasswordAlgorithm,
71    ) -> Option<Password> {
72        log::info!(
73            "get password: address={:?}, interface={:?}, transport={:?}, username={:?}, algorithm={:?}",
74            id.source,
75            id.interface,
76            id.transport,
77            username,
78            algorithm
79        );
80
81        // Match the static authentication information first.
82        if let Some(password) = self.config.auth.static_credentials.get(username) {
83            return Some(generate_password(
84                username,
85                password,
86                &self.config.server.realm,
87                algorithm,
88            ));
89        }
90
91        // Try again to match the static authentication key.
92        if let Some(secret) = &self.config.auth.static_auth_secret {
93            return Some(static_auth_secret(
94                username,
95                secret,
96                &self.config.server.realm,
97                algorithm,
98            ));
99        }
100
101        #[cfg(feature = "api")]
102        if self.config.auth.enable_hooks_auth {
103            return self
104                .rpc
105                .get_password(id, &self.config.server.realm, username, algorithm)
106                .await;
107        }
108
109        None
110    }
111
112    /// allocate request
113    ///
114    /// [rfc8489](https://tools.ietf.org/html/rfc8489)
115    ///
116    /// In all cases, the server SHOULD only allocate ports from the range
117    /// 49152 - 65535 (the Dynamic and/or Private Port range [PORT-NUMBERS]),
118    /// unless the TURN server application knows, through some means not
119    /// specified here, that other applications running on the same host as
120    /// the TURN server application will not be impacted by allocating ports
121    /// outside this range.  This condition can often be satisfied by running
122    /// the TURN server application on a dedicated machine and/or by
123    /// arranging that any other applications on the machine allocate ports
124    /// before the TURN server application starts.  In any case, the TURN
125    /// server SHOULD NOT allocate ports in the range 0 - 1023 (the Well-
126    /// Known Port range) to discourage clients from using TURN to run
127    /// standard services.
128    fn on_allocated(&self, id: &Identifier, username: &str, port: u16) {
129        log::info!(
130            "allocate: address={:?}, interface={:?}, transport={:?}, username={:?}, port={}",
131            id.source,
132            id.interface,
133            id.transport,
134            username,
135            port
136        );
137
138        #[cfg(feature = "api")]
139        {
140            self.statistics.register(*id);
141
142            self.rpc
143                .send_event(HooksEvent::Allocated(TurnAllocatedEvent {
144                    id: Some(id.into()),
145                    username: username.to_string(),
146                    port: port as i32,
147                }));
148        }
149    }
150
151    /// channel binding request
152    ///
153    /// The server MAY impose restrictions on the IP address and port values
154    /// allowed in the XOR-PEER-ADDRESS attribute; if a value is not allowed,
155    /// the server rejects the request with a 403 (Forbidden) error.
156    ///
157    /// If the request is valid, but the server is unable to fulfill the
158    /// request due to some capacity limit or similar, the server replies
159    /// with a 508 (Insufficient Capacity) error.
160    ///
161    /// Otherwise, the server replies with a ChannelBind success response.
162    /// There are no required attributes in a successful ChannelBind
163    /// response.
164    ///
165    /// If the server can satisfy the request, then the server creates or
166    /// refreshes the channel binding using the channel number in the
167    /// CHANNEL-NUMBER attribute and the interface address in the XOR-PEER-
168    /// ADDRESS attribute.  The server also installs or refreshes a
169    /// permission for the IP address in the XOR-PEER-ADDRESS attribute as
170    /// described in Section 9.
171    ///
172    /// NOTE: A server need not do anything special to implement
173    /// idempotency of ChannelBind requests over UDP using the
174    /// "stateless stack approach".  Retransmitted ChannelBind requests
175    /// will simply refresh the channel binding and the corresponding
176    /// permission.  Furthermore, the client must wait 5 minutes before
177    /// binding a previously bound channel number or peer address to a
178    /// different channel, eliminating the possibility that the
179    /// transaction would initially fail but succeed on a
180    /// retransmission.
181    fn on_channel_bind(&self, id: &Identifier, username: &str, channel: u16) {
182        log::info!(
183            "channel bind: address={:?}, interface={:?}, transport={:?}, username={:?}, channel={}",
184            id.source,
185            id.interface,
186            id.transport,
187            username,
188            channel
189        );
190
191        #[cfg(feature = "api")]
192        {
193            self.rpc
194                .send_event(HooksEvent::ChannelBind(TurnChannelBindEvent {
195                    id: Some(id.into()),
196                    username: username.to_string(),
197                    channel: channel as i32,
198                }));
199        }
200    }
201
202    /// create permission request
203    ///
204    /// [rfc8489](https://tools.ietf.org/html/rfc8489)
205    ///
206    /// When the server receives the CreatePermission request, it processes
207    /// as per [Section 5](https://tools.ietf.org/html/rfc8656#section-5)
208    /// plus the specific rules mentioned here.
209    ///
210    /// The message is checked for validity.  The CreatePermission request
211    /// MUST contain at least one XOR-PEER-ADDRESS attribute and MAY contain
212    /// multiple such attributes.  If no such attribute exists, or if any of
213    /// these attributes are invalid, then a 400 (Bad Request) error is
214    /// returned.  If the request is valid, but the server is unable to
215    /// satisfy the request due to some capacity limit or similar, then a 508
216    /// (Insufficient Capacity) error is returned.
217    ///
218    /// If an XOR-PEER-ADDRESS attribute contains an address of an address
219    /// family that is not the same as that of a relayed interface address
220    /// for the allocation, the server MUST generate an error response with
221    /// the 443 (Peer Address Family Mismatch) response code.
222    ///
223    /// The server MAY impose restrictions on the IP address allowed in the
224    /// XOR-PEER-ADDRESS attribute; if a value is not allowed, the server
225    /// rejects the request with a 403 (Forbidden) error.
226    ///
227    /// If the message is valid and the server is capable of carrying out the
228    /// request, then the server installs or refreshes a permission for the
229    /// IP address contained in each XOR-PEER-ADDRESS attribute as described
230    /// in [Section 9](https://tools.ietf.org/html/rfc8656#section-9).  
231    /// The port portion of each attribute is ignored and may be any arbitrary
232    /// value.
233    ///
234    /// The server then responds with a CreatePermission success response.
235    /// There are no mandatory attributes in the success response.
236    ///
237    /// NOTE: A server need not do anything special to implement
238    /// idempotency of CreatePermission requests over UDP using the
239    /// "stateless stack approach".  Retransmitted CreatePermission
240    /// requests will simply refresh the permissions.
241    fn on_create_permission(&self, id: &Identifier, username: &str, ports: &[u16]) {
242        log::info!(
243            "create permission: address={:?}, interface={:?}, transport={:?}, username={:?}, ports={:?}",
244            id.source,
245            id.interface,
246            id.transport,
247            username,
248            ports
249        );
250
251        #[cfg(feature = "api")]
252        {
253            self.rpc
254                .send_event(HooksEvent::CreatePermission(TurnCreatePermissionEvent {
255                    id: Some(id.into()),
256                    username: username.to_string(),
257                    ports: ports.iter().map(|p| *p as i32).collect(),
258                }));
259        }
260    }
261
262    /// refresh request
263    ///
264    /// If the server receives a Refresh Request with a REQUESTED-ADDRESS-
265    /// FAMILY attribute and the attribute value does not match the address
266    /// family of the allocation, the server MUST reply with a 443 (Peer
267    /// Address Family Mismatch) Refresh error response.
268    ///
269    /// The server computes a value called the "desired lifetime" as follows:
270    /// if the request contains a LIFETIME attribute and the attribute value
271    /// is zero, then the "desired lifetime" is zero.  Otherwise, if the
272    /// request contains a LIFETIME attribute, then the server computes the
273    /// minimum of the client's requested lifetime and the server's maximum
274    /// allowed lifetime.  If this computed value is greater than the default
275    /// lifetime, then the "desired lifetime" is the computed value.
276    /// Otherwise, the "desired lifetime" is the default lifetime.
277    ///
278    /// Subsequent processing depends on the "desired lifetime" value:
279    ///
280    /// * If the "desired lifetime" is zero, then the request succeeds and
281    ///   the allocation is deleted.
282    ///
283    /// * If the "desired lifetime" is non-zero, then the request succeeds
284    ///   and the allocation's time-to-expiry is set to the "desired
285    ///   lifetime".
286    ///
287    /// If the request succeeds, then the server sends a success response
288    /// containing:
289    ///
290    /// * A LIFETIME attribute containing the current value of the time-to-
291    ///   expiry timer.
292    ///
293    /// NOTE: A server need not do anything special to implement
294    /// idempotency of Refresh requests over UDP using the "stateless
295    /// stack approach".  Retransmitted Refresh requests with a non-
296    /// zero "desired lifetime" will simply refresh the allocation.  A
297    /// retransmitted Refresh request with a zero "desired lifetime"
298    /// will cause a 437 (Allocation Mismatch) response if the
299    /// allocation has already been deleted, but the client will treat
300    /// this as equivalent to a success response (see below).
301    fn on_refresh(&self, id: &Identifier, username: &str, lifetime: u32) {
302        log::info!(
303            "refresh: address={:?}, interface={:?}, transport={:?}, username={:?}, lifetime={}",
304            id.source,
305            id.interface,
306            id.transport,
307            username,
308            lifetime
309        );
310
311        #[cfg(feature = "api")]
312        {
313            self.rpc.send_event(HooksEvent::Refresh(TurnRefreshEvent {
314                id: Some(id.into()),
315                username: username.to_string(),
316                lifetime: lifetime as i32,
317            }));
318        }
319    }
320
321    /// session closed
322    ///
323    /// Triggered when the session leaves from the turn. Possible reasons: the
324    /// session life cycle has expired, external active deletion, or active
325    /// exit of the session.
326    fn on_destroy(&self, id: &Identifier, username: &str) {
327        log::info!(
328            "closed: address={:?}, interface={:?}, transport={:?}, username={:?}",
329            id.source,
330            id.interface,
331            id.transport,
332            username
333        );
334
335        #[cfg(feature = "api")]
336        {
337            self.statistics.unregister(id);
338
339            self.rpc.send_event(HooksEvent::Destroy(TurnDestroyEvent {
340                id: Some(id.into()),
341                username: username.to_string(),
342            }));
343        }
344    }
345}