Skip to main content

tuwunel_api/client/
voip.rs

1use std::time::{Duration, SystemTime};
2
3use axum::extract::State;
4use base64::{Engine as _, engine::general_purpose};
5use hmac::{Hmac, Mac};
6use ruma::{SecondsSinceUnixEpoch, api::client::voip::get_turn_server_info};
7use sha1::Sha1;
8use tuwunel_core::{Err, Result};
9
10use crate::Ruma;
11
12type HmacSha1 = Hmac<Sha1>;
13
14/// # `GET /_matrix/client/r0/voip/turnServer`
15///
16/// TODO: Returns information about the recommended turn server.
17pub(crate) async fn turn_server_route(
18	State(services): State<crate::State>,
19	body: Ruma<get_turn_server_info::v3::Request>,
20) -> Result<get_turn_server_info::v3::Response> {
21	// MSC4166: return M_NOT_FOUND 404 if no TURN URIs are specified in any way
22	if services.server.config.turn_uris.is_empty() {
23		return Err!(Request(NotFound("Not Found")));
24	}
25
26	let user = body.sender_user();
27
28	if !services.config.turn_allow_guests
29		&& body.appservice_info.is_none()
30		&& services
31			.users
32			.is_deactivated(user)
33			.await
34			.unwrap_or(false)
35	{
36		return Err!(Request(Forbidden("Guest users are not allowed to get TURN credentials")));
37	}
38
39	let turn_secret = &services.globals.turn_secret;
40
41	let (username, password) = if let Some(turn_secret) = turn_secret {
42		let expiry = SecondsSinceUnixEpoch::from_system_time(
43			SystemTime::now()
44				.checked_add(Duration::from_secs(services.config.turn_ttl))
45				.expect("TURN TTL should not get this high"),
46		)
47		.expect("time is valid");
48
49		let username: String = format!("{}:{}", expiry.get(), user);
50		let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
51			.expect("HMAC can take key of any size");
52
53		mac.update(username.as_bytes());
54		let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
55
56		(username, password)
57	} else {
58		(services.config.turn_username.clone(), services.config.turn_password.clone())
59	};
60
61	Ok(get_turn_server_info::v3::Response {
62		username,
63		password,
64		uris: services.config.turn_uris.clone(),
65		ttl: Duration::from_secs(services.config.turn_ttl),
66	})
67}