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