turn_server/
handler.rs

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