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::STANDARD};
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 (username, password) = services.globals.turn_secret().map_or_else(
40		|| (services.config.turn_username.clone(), services.config.turn_password.clone()),
41		|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 = 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 = STANDARD.encode(mac.finalize().into_bytes());
55
56			(username, password)
57		},
58	);
59
60	Ok(get_turn_server_info::v3::Response {
61		username,
62		password,
63		uris: services.config.turn_uris.clone(),
64		ttl: Duration::from_secs(services.config.turn_ttl),
65	})
66}