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}