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, utils::sender_is_guest};
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	let provided = services.config.turn.provider().is_some();
22
23	// MSC4166: return M_NOT_FOUND 404 if no TURN URIs are specified in any way.
24	// A configured provider that cannot be reached answers 502, as that is transient.
25	if !provided && services.server.config.turn.uris.is_empty() {
26		return Err!(Request(NotFound("Not Found")));
27	}
28
29	let user = body.sender_user();
30
31	if !services.config.turn.allow_guests && sender_is_guest(&services, &body).await {
32		return Err!(Request(Forbidden("Guest users are not allowed to get TURN credentials")));
33	}
34
35	if provided {
36		return services.turn.credentials().await;
37	}
38
39	let (username, password) = services
40		.globals
41		.turn_secret()
42		.map(|turn_secret| {
43			let expiry = SecondsSinceUnixEpoch::from_system_time(
44				SystemTime::now()
45					.checked_add(Duration::from_secs(services.config.turn.ttl))
46					.expect("TURN TTL should not get this high"),
47			)
48			.expect("time is valid");
49
50			let username = format!("{}:{}", expiry.get(), user);
51			let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
52				.expect("HMAC can take key of any size");
53
54			mac.update(username.as_bytes());
55			let password = STANDARD.encode(mac.finalize().into_bytes());
56
57			(username, password)
58		})
59		.unwrap_or_else(|| {
60			(services.config.turn.username.clone(), services.config.turn.password.clone())
61		});
62
63	Ok(get_turn_server_info::v3::Response {
64		username,
65		password,
66		uris: services.config.turn.uris.clone(),
67		ttl: Duration::from_secs(services.config.turn.ttl),
68	})
69}