diff --git a/src/admin/room/moderation.rs b/src/admin/room/moderation.rs index 68a90efe..f370b13e 100644 --- a/src/admin/room/moderation.rs +++ b/src/admin/room/moderation.rs @@ -48,7 +48,7 @@ pub(crate) enum RoomModerationCommand { async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result { debug!("Got room alias or ID: {}", room); - let admin_room_alias = &self.services.globals.admin_alias; + let admin_room_alias = &self.services.admin.admin_alias; if let Ok(admin_room_id) = self.services.admin.get_admin_room().await { if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) { @@ -209,7 +209,7 @@ async fn ban_list_of_rooms(&self) -> Result { .drain(1..self.body.len().saturating_sub(1)) .collect::>(); - let admin_room_alias = &self.services.globals.admin_alias; + let admin_room_alias = &self.services.admin.admin_alias; let mut room_ban_count: usize = 0; let mut room_ids: Vec = Vec::new(); diff --git a/src/api/client/alias.rs b/src/api/client/alias.rs index 9e022536..7976d347 100644 --- a/src/api/client/alias.rs +++ b/src/api/client/alias.rs @@ -26,8 +26,8 @@ pub(crate) async fn create_alias_route( // this isn't apart of alias_checks or delete alias route because we should // allow removing forbidden room aliases if services - .globals - .forbidden_alias_names() + .config + .forbidden_alias_names .is_match(body.room_alias.alias()) { return Err!(Request(Forbidden("Room alias is forbidden."))); diff --git a/src/api/client/register.rs b/src/api/client/register.rs index 6e40b7f0..3f012180 100644 --- a/src/api/client/register.rs +++ b/src/api/client/register.rs @@ -58,8 +58,8 @@ pub(crate) async fn get_register_available_route( }); if services - .globals - .forbidden_usernames() + .config + .forbidden_usernames .is_match(&body.username) { return Err!(Request(Forbidden("Username is forbidden"))); @@ -228,8 +228,8 @@ pub(crate) async fn register_route( }); if services - .globals - .forbidden_usernames() + .config + .forbidden_usernames .is_match(username) && !emergency_mode_enabled { @@ -384,8 +384,8 @@ pub(crate) async fn register_route( // If `new_user_displayname_suffix` is set, registration will push whatever // content is set to the user's display name with a space before it if !services - .globals - .new_user_displayname_suffix() + .config + .new_user_displayname_suffix .is_empty() && body.appservice_info.is_none() { diff --git a/src/api/client/room/create.rs b/src/api/client/room/create.rs index ea634e50..ae645320 100644 --- a/src/api/client/room/create.rs +++ b/src/api/client/room/create.rs @@ -678,8 +678,8 @@ async fn room_alias_check( // check if room alias is forbidden if services - .globals - .forbidden_alias_names() + .config + .forbidden_alias_names .is_match(room_alias_name) { return Err!(Request(Unknown("Room alias name is forbidden."))); @@ -725,8 +725,8 @@ async fn room_alias_check( async fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result { // apply forbidden room alias checks to custom room IDs too if services - .globals - .forbidden_alias_names() + .config + .forbidden_alias_names .is_match(custom_room_id) { return Err!(Request(Unknown("Custom room ID is forbidden."))); @@ -798,7 +798,7 @@ async fn can_create_room_check( services: &Services, body: &Ruma, ) -> Result { - if !services.globals.allow_room_creation() + if !services.config.allow_room_creation && body.appservice_info.is_none() && !services.users.is_admin(body.sender_user()).await { diff --git a/src/api/client/voip.rs b/src/api/client/voip.rs index 41a36154..0f21c71c 100644 --- a/src/api/client/voip.rs +++ b/src/api/client/voip.rs @@ -25,12 +25,12 @@ pub(crate) async fn turn_server_route( return Err!(Request(NotFound("Not Found"))); } - let turn_secret = services.globals.turn_secret.clone(); + let turn_secret = &services.config.turn_secret; let (username, password) = if !turn_secret.is_empty() { let expiry = SecondsSinceUnixEpoch::from_system_time( SystemTime::now() - .checked_add(Duration::from_secs(services.globals.turn_ttl())) + .checked_add(Duration::from_secs(services.config.turn_ttl)) .expect("TURN TTL should not get this high"), ) .expect("time is valid"); @@ -53,16 +53,13 @@ pub(crate) async fn turn_server_route( (username, password) } else { - ( - services.globals.turn_username().clone(), - services.globals.turn_password().clone(), - ) + (services.config.turn_username.clone(), services.config.turn_password.clone()) }; Ok(get_turn_server_info::v3::Response { username, password, - uris: services.globals.turn_uris().to_vec(), - ttl: Duration::from_secs(services.globals.turn_ttl()), + uris: services.config.turn_uris.clone(), + ttl: Duration::from_secs(services.config.turn_ttl), }) } diff --git a/src/api/server/publicrooms.rs b/src/api/server/publicrooms.rs index 5a010f02..59803bb7 100644 --- a/src/api/server/publicrooms.rs +++ b/src/api/server/publicrooms.rs @@ -59,8 +59,8 @@ pub(crate) async fn get_public_rooms_route( body: Ruma, ) -> Result { if !services - .globals - .allow_public_room_directory_over_federation() + .config + .allow_public_room_directory_over_federation { return Err(Error::BadRequest(ErrorKind::forbidden(), "Room directory is not public")); } diff --git a/src/api/server/user.rs b/src/api/server/user.rs index 406ac72d..fafb929e 100644 --- a/src/api/server/user.rs +++ b/src/api/server/user.rs @@ -44,7 +44,7 @@ pub(crate) async fn get_devices_route( let device_id = metadata.device_id.clone(); let device_id_clone = device_id.clone(); let device_id_string = device_id.as_str().to_owned(); - let device_display_name = if services.globals.allow_device_name_federation() { + let device_display_name = if services.config.allow_device_name_federation { metadata.display_name.clone() } else { Some(device_id_string) @@ -95,7 +95,7 @@ pub(crate) async fn get_keys_route( None, &body.device_keys, |u| Some(u.server_name()) == body.origin.as_deref(), - services.globals.allow_device_name_federation(), + services.config.allow_device_name_federation, ) .await?; diff --git a/src/service/admin/create.rs b/src/service/admin/create.rs index b3b6483e..06900b2d 100644 --- a/src/service/admin/create.rs +++ b/src/service/admin/create.rs @@ -185,7 +185,7 @@ pub async fn create_admin_room(services: &Services) -> Result { .await?; // 6. Room alias - let alias = &services.globals.admin_alias; + let alias = &services.admin.admin_alias; services .timeline diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 6fd0353d..6c6da30e 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -12,7 +12,7 @@ use async_trait::async_trait; pub use create::create_admin_room; use futures::{Future, FutureExt, TryFutureExt}; use ruma::{ - OwnedEventId, OwnedRoomId, RoomId, UserId, + OwnedEventId, OwnedRoomAliasId, OwnedRoomId, RoomId, UserId, events::room::message::{Relation, RoomMessageEventContent}, }; use tokio::sync::{RwLock, mpsc}; @@ -27,6 +27,7 @@ pub struct Service { channel: StdRwLock>>, pub handle: RwLock>, pub complete: StdRwLock>, + pub admin_alias: OwnedRoomAliasId, #[cfg(feature = "console")] pub console: Arc, } @@ -69,6 +70,8 @@ impl crate::Service for Service { channel: StdRwLock::new(None), handle: RwLock::new(None), complete: StdRwLock::new(None), + admin_alias: OwnedRoomAliasId::try_from(format!("#admins:{}", &args.server.name)) + .expect("#admins:server_name is valid alias name"), #[cfg(feature = "console")] console: console::Console::new(args), })) @@ -234,7 +237,7 @@ impl Service { let room_id = self .services .alias - .resolve_local_alias(&self.services.globals.admin_alias) + .resolve_local_alias(&self.admin_alias) .await?; self.services diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 092f8519..4637770e 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -1,20 +1,10 @@ mod data; -use std::{ - collections::HashMap, - fmt::Write, - ops::Range, - sync::{Arc, RwLock}, - time::Instant, -}; +use std::{ops::Range, sync::Arc}; -use async_trait::async_trait; use data::Data; -use regex::RegexSet; -use ruma::{ - OwnedEventId, OwnedRoomAliasId, OwnedServerName, OwnedUserId, RoomAliasId, ServerName, UserId, -}; -use tuwunel_core::{Result, Server, error, utils::bytes::pretty}; +use ruma::{OwnedUserId, RoomAliasId, ServerName, UserId}; +use tuwunel_core::{Result, Server, error}; use crate::service; @@ -22,16 +12,11 @@ pub struct Service { pub db: Data, server: Arc, - pub bad_event_ratelimiter: Arc>>, pub server_user: OwnedUserId, - pub admin_alias: OwnedRoomAliasId, pub turn_secret: String, pub registration_token: Option, } -type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries - -#[async_trait] impl crate::Service for Service { fn build(args: &crate::Args<'_>) -> Result> { let db = Data::new(args); @@ -67,9 +52,6 @@ impl crate::Service for Service { Ok(Arc::new(Self { db, server: args.server.clone(), - bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), - admin_alias: OwnedRoomAliasId::try_from(format!("#admins:{}", &args.server.name)) - .expect("#admins:server_name is valid alias name"), server_user: UserId::parse_with_server_name( String::from("conduit"), &args.server.name, @@ -80,29 +62,6 @@ impl crate::Service for Service { })) } - async fn memory_usage(&self, out: &mut (dyn Write + Send)) -> Result { - let (ber_count, ber_bytes) = self.bad_event_ratelimiter.read()?.iter().fold( - (0_usize, 0_usize), - |(mut count, mut bytes), (event_id, _)| { - bytes = bytes.saturating_add(event_id.capacity()); - bytes = bytes.saturating_add(size_of::()); - count = count.saturating_add(1); - (count, bytes) - }, - ); - - writeln!(out, "bad_event_ratelimiter: {ber_count} ({})", pretty(ber_bytes))?; - - Ok(()) - } - - async fn clear_cache(&self) { - self.bad_event_ratelimiter - .write() - .expect("locked for writing") - .clear(); - } - fn name(&self) -> &str { service::make_name(std::module_path!()) } } @@ -141,110 +100,6 @@ impl Service { #[must_use] pub fn server_name(&self) -> &ServerName { self.server.name.as_ref() } - #[inline] - #[must_use] - pub fn allow_public_room_directory_over_federation(&self) -> bool { - self.server - .config - .allow_public_room_directory_over_federation - } - - #[inline] - #[must_use] - pub fn allow_device_name_federation(&self) -> bool { - self.server.config.allow_device_name_federation - } - - #[inline] - #[must_use] - pub fn allow_room_creation(&self) -> bool { self.server.config.allow_room_creation } - - #[inline] - #[must_use] - pub fn new_user_displayname_suffix(&self) -> &String { - &self.server.config.new_user_displayname_suffix - } - - #[inline] - #[must_use] - pub fn trusted_servers(&self) -> &[OwnedServerName] { &self.server.config.trusted_servers } - - #[inline] - #[must_use] - pub fn turn_password(&self) -> &String { &self.server.config.turn_password } - - #[inline] - #[must_use] - pub fn turn_ttl(&self) -> u64 { self.server.config.turn_ttl } - - #[inline] - #[must_use] - pub fn turn_uris(&self) -> &[String] { &self.server.config.turn_uris } - - #[inline] - #[must_use] - pub fn turn_username(&self) -> &String { &self.server.config.turn_username } - - #[inline] - #[must_use] - pub fn notification_push_path(&self) -> &String { &self.server.config.notification_push_path } - - #[inline] - #[must_use] - pub fn url_preview_domain_contains_allowlist(&self) -> &Vec { - &self - .server - .config - .url_preview_domain_contains_allowlist - } - - #[inline] - #[must_use] - pub fn url_preview_domain_explicit_allowlist(&self) -> &Vec { - &self - .server - .config - .url_preview_domain_explicit_allowlist - } - - #[inline] - #[must_use] - pub fn url_preview_domain_explicit_denylist(&self) -> &Vec { - &self - .server - .config - .url_preview_domain_explicit_denylist - } - - #[inline] - #[must_use] - pub fn url_preview_url_contains_allowlist(&self) -> &Vec { - &self - .server - .config - .url_preview_url_contains_allowlist - } - - #[inline] - #[must_use] - pub fn url_preview_max_spider_size(&self) -> usize { - self.server.config.url_preview_max_spider_size - } - - #[inline] - #[must_use] - pub fn url_preview_check_root_domain(&self) -> bool { - self.server.config.url_preview_check_root_domain - } - - #[inline] - #[must_use] - pub fn forbidden_alias_names(&self) -> &RegexSet { &self.server.config.forbidden_alias_names } - - #[inline] - #[must_use] - pub fn forbidden_usernames(&self) -> &RegexSet { &self.server.config.forbidden_usernames } - /// checks if `user_id` is local to us via server_name comparison #[inline] #[must_use] diff --git a/src/service/media/preview.rs b/src/service/media/preview.rs index 0253eac0..c8c75afa 100644 --- a/src/service/media/preview.rs +++ b/src/service/media/preview.rs @@ -180,20 +180,12 @@ async fn download_html(&self, url: &str) -> Result { let mut bytes: Vec = Vec::new(); while let Some(chunk) = response.chunk().await? { bytes.extend_from_slice(&chunk); - if bytes.len() - > self - .services - .globals - .url_preview_max_spider_size() - { + if bytes.len() > self.services.config.url_preview_max_spider_size { debug!( "Response body from URL {} exceeds url_preview_max_spider_size ({}), not \ processing the rest of the response body and assuming our necessary data is in \ this range.", - url, - self.services - .globals - .url_preview_max_spider_size() + url, self.services.config.url_preview_max_spider_size ); break; } @@ -244,22 +236,22 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool { | Some(h) => h.to_owned(), }; - let allowlist_domain_contains = self + let allowlist_domain_contains = &self .services - .globals - .url_preview_domain_contains_allowlist(); - let allowlist_domain_explicit = self + .config + .url_preview_domain_contains_allowlist; + let allowlist_domain_explicit = &self .services - .globals - .url_preview_domain_explicit_allowlist(); - let denylist_domain_explicit = self + .config + .url_preview_domain_explicit_allowlist; + let denylist_domain_explicit = &self .services - .globals - .url_preview_domain_explicit_denylist(); - let allowlist_url_contains = self + .config + .url_preview_domain_explicit_denylist; + let allowlist_url_contains = &self .services - .globals - .url_preview_url_contains_allowlist(); + .config + .url_preview_url_contains_allowlist; if allowlist_domain_contains.contains(&"*".to_owned()) || allowlist_domain_explicit.contains(&"*".to_owned()) @@ -306,11 +298,7 @@ pub fn url_preview_allowed(&self, url: &Url) -> bool { } // check root domain if available and if user has root domain checks - if self - .services - .globals - .url_preview_check_root_domain() - { + if self.services.config.url_preview_check_root_domain { debug!("Checking root domain"); match host.split_once('.') { | None => return false, diff --git a/src/service/migrations.rs b/src/service/migrations.rs index b218e3fb..5a4b54df 100644 --- a/src/service/migrations.rs +++ b/src/service/migrations.rs @@ -159,7 +159,7 @@ async fn migrate(services: &Services) -> Result { ); { - let patterns = services.globals.forbidden_usernames(); + let patterns = &services.config.forbidden_usernames; if !patterns.is_empty() { services .users @@ -183,7 +183,7 @@ async fn migrate(services: &Services) -> Result { } { - let patterns = services.globals.forbidden_alias_names(); + let patterns = &services.config.forbidden_alias_names; if !patterns.is_empty() { for room_id in services .metadata diff --git a/src/service/pusher/mod.rs b/src/service/pusher/mod.rs index 071b4eec..8fc8dba8 100644 --- a/src/service/pusher/mod.rs +++ b/src/service/pusher/mod.rs @@ -207,7 +207,7 @@ impl Service { features: Default::default(), }; - let dest = dest.replace(self.services.globals.notification_push_path(), ""); + let dest = dest.replace(&self.services.config.notification_push_path, ""); trace!("Push gateway destination: {dest}"); let http_request = request diff --git a/src/service/rooms/alias/mod.rs b/src/service/rooms/alias/mod.rs index d255a42f..3e69cee0 100644 --- a/src/service/rooms/alias/mod.rs +++ b/src/service/rooms/alias/mod.rs @@ -45,7 +45,7 @@ impl Service { pub fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId, user_id: &UserId) -> Result { self.check_alias_local(alias)?; - if alias == self.services.globals.admin_alias + if alias == self.services.admin.admin_alias && user_id != self.services.globals.server_user { return Err!(Request(Forbidden("Only the server user can set this alias"))); diff --git a/src/service/rooms/event_handler/mod.rs b/src/service/rooms/event_handler/mod.rs index ee95c167..31952a25 100644 --- a/src/service/rooms/event_handler/mod.rs +++ b/src/service/rooms/event_handler/mod.rs @@ -11,34 +11,38 @@ mod state_at_incoming; mod upgrade_outlier_pdu; use std::{ - collections::hash_map, + collections::{HashMap, hash_map}, fmt::Write, ops::Range, - sync::Arc, + sync::{Arc, RwLock}, time::{Duration, Instant}, }; use async_trait::async_trait; -use ruma::{EventId, OwnedRoomId, RoomId}; +use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId}; use tuwunel_core::{ Err, Result, implement, matrix::{Event, PduEvent}, - utils::{MutexMap, continue_exponential_backoff}, + utils::{MutexMap, bytes::pretty, continue_exponential_backoff}, }; +type RoomMutexMap = MutexMap; + +type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries + pub struct Service { pub mutex_federation: RoomMutexMap, services: Arc, + bad_event_ratelimiter: Arc>>, } -type RoomMutexMap = MutexMap; - #[async_trait] impl crate::Service for Service { fn build(args: &crate::Args<'_>) -> Result> { Ok(Arc::new(Self { mutex_federation: RoomMutexMap::new(), services: args.services.clone(), + bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), })) } @@ -46,9 +50,28 @@ impl crate::Service for Service { let mutex_federation = self.mutex_federation.len(); writeln!(out, "federation_mutex: {mutex_federation}")?; + let (ber_count, ber_bytes) = self.bad_event_ratelimiter.read()?.iter().fold( + (0_usize, 0_usize), + |(mut count, mut bytes), (event_id, _)| { + bytes = bytes.saturating_add(event_id.capacity()); + bytes = bytes.saturating_add(size_of::()); + count = count.saturating_add(1); + (count, bytes) + }, + ); + + writeln!(out, "bad_event_ratelimiter: {ber_count} ({})", pretty(ber_bytes))?; + Ok(()) } + async fn clear_cache(&self) { + self.bad_event_ratelimiter + .write() + .expect("locked for writing") + .clear(); + } + fn name(&self) -> &str { crate::service::make_name(std::module_path!()) } } @@ -57,8 +80,6 @@ fn back_off(&self, event_id: &EventId) { use hash_map::Entry::{Occupied, Vacant}; match self - .services - .globals .bad_event_ratelimiter .write() .expect("locked") @@ -76,8 +97,6 @@ fn back_off(&self, event_id: &EventId) { #[implement(Service)] fn is_backed_off(&self, event_id: &EventId, range: Range) -> bool { let Some((time, tries)) = self - .services - .globals .bad_event_ratelimiter .read() .expect("locked") diff --git a/src/service/server_keys/acquire.rs b/src/service/server_keys/acquire.rs index 1a6d374b..8238f721 100644 --- a/src/service/server_keys/acquire.rs +++ b/src/service/server_keys/acquire.rs @@ -212,7 +212,7 @@ where I: Iterator)> + Send, { let mut missing: Batch = batch.collect(); - for notary in self.services.globals.trusted_servers() { + for notary in &self.services.config.trusted_servers { let missing_keys = keys_count(&missing); let missing_servers = missing.len(); debug!( diff --git a/src/service/server_keys/get.rs b/src/service/server_keys/get.rs index 71cfb0d7..ac5f7f0c 100644 --- a/src/service/server_keys/get.rs +++ b/src/service/server_keys/get.rs @@ -121,7 +121,7 @@ async fn get_verify_key_from_notaries( origin: &ServerName, key_id: &ServerSigningKeyId, ) -> Result { - for notary in self.services.globals.trusted_servers() { + for notary in &self.services.config.trusted_servers { if let Ok(server_keys) = self.notary_request(notary, origin).await { for server_key in server_keys.clone() { self.add_signing_keys(server_key).await;