diff --git a/conduwuit-example.toml b/conduwuit-example.toml index aee29f928..72345377e 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1117,6 +1117,16 @@ # #ip_range_denylist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", +# Optional interface to bind to with SO_BINDTODEVICE for URL previews. +# If not set, it will not bind to a specific interface. +# This uses [`reqwest::ClientBuilder::interface`] under the hood. +# +# To list the interfaces on your system, use the command `ip link show` +# +# Example: `"eth0"` +# +#url_preview_bound_interface = false + # Vector list of domains allowed to send requests to for URL previews. # Defaults to none. Note: this is a *contains* match, not an explicit # match. Putting "google.com" will match "https://google.com" and diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index cb8f3b876..50e620c43 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1250,6 +1250,17 @@ pub struct Config { #[serde(default = "default_ip_range_denylist")] pub ip_range_denylist: Vec, + /// Optional interface to bind to with SO_BINDTODEVICE for URL previews. + /// If not set, it will not bind to a specific interface. + /// This uses [`reqwest::ClientBuilder::interface`] under the hood. + /// + /// To list the interfaces on your system, use the command `ip link show` + /// + /// Example: `"eth0"` + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + #[serde(default)] + pub url_preview_bound_interface: Option, + /// Vector list of domains allowed to send requests to for URL previews. /// Defaults to none. Note: this is a *contains* match, not an explicit /// match. Putting "google.com" will match "https://google.com" and @@ -1960,6 +1971,15 @@ impl fmt::Display for Config { line("Forbidden room aliases", { &self.forbidden_alias_names.patterns().iter().join(", ") }); + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + line( + "URL preview bound interface", + if let Some(interface) = &self.url_preview_bound_interface { + interface + } else { + "not set" + }, + ); line( "URL preview domain contains allowlist", &self.url_preview_domain_contains_allowlist.join(", "), diff --git a/src/service/client/mod.rs b/src/service/client/mod.rs index 2794efc1b..0b3bfd653 100644 --- a/src/service/client/mod.rs +++ b/src/service/client/mod.rs @@ -25,15 +25,21 @@ impl crate::Service for Service { let config = &args.server.config; let resolver = args.require::("resolver"); + let mut url_preview_builder = base(config)? + .dns_resolver(resolver.resolver.clone()) + .redirect(redirect::Policy::limited(3)); + + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + if let Some(interface) = &config.url_preview_bound_interface { + url_preview_builder = url_preview_builder.interface(&interface); + } + Ok(Arc::new(Self { default: base(config)? .dns_resolver(resolver.resolver.clone()) .build()?, - url_preview: base(config)? - .dns_resolver(resolver.resolver.clone()) - .redirect(redirect::Policy::limited(3)) - .build()?, + url_preview: url_preview_builder.build()?, extern_media: base(config)? .dns_resolver(resolver.resolver.clone()) diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 4fb1ce2d1..0beb40fe6 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -198,6 +198,9 @@ impl Service { pub fn emergency_password(&self) -> &Option { &self.config.emergency_password } + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + pub fn url_preview_bound_interface(&self) -> &Option { &self.config.url_preview_bound_interface } + pub fn url_preview_domain_contains_allowlist(&self) -> &Vec { &self.config.url_preview_domain_contains_allowlist }