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}