mirror of
https://github.com/girlbossceo/conduwuit.git
synced 2025-03-14 18:55:37 +00:00
extend x-platform support for binding URL previews to interfaces via address
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
52cee65748
commit
f0a1aaf7bc
6 changed files with 74 additions and 28 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -824,6 +824,7 @@ dependencies = [
|
|||
"conduit_core",
|
||||
"conduit_database",
|
||||
"const-str",
|
||||
"either",
|
||||
"futures",
|
||||
"hickory-resolver",
|
||||
"http",
|
||||
|
|
|
@ -1117,13 +1117,15 @@
|
|||
#
|
||||
#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.
|
||||
# Optional IP address or network interface-name to bind as the source of
|
||||
# URL preview requests. If not set, it will not bind to a specific
|
||||
# address or interface.
|
||||
#
|
||||
# To list the interfaces on your system, use the command `ip link show`
|
||||
# Interface names only supported on Linux, Android, and Fuchsia platforms;
|
||||
# all other platforms can specify the IP address. To list the interfaces
|
||||
# on your system, use the command `ip link show`.
|
||||
#
|
||||
# Example: `"eth0"`
|
||||
# example: `"eth0"` or `"1.2.3.4"`
|
||||
#
|
||||
#url_preview_bound_interface =
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use std::env::consts::OS;
|
||||
|
||||
use either::Either;
|
||||
use figment::Figment;
|
||||
|
||||
use super::DEPRECATED_KEYS;
|
||||
|
@ -191,6 +194,15 @@ For security and safety reasons, conduwuit will shut down. If you are extra sure
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(Either::Right(_)) = config.url_preview_bound_interface.as_ref() {
|
||||
if !matches!(OS, "android" | "fuchsia" | "linux") {
|
||||
return Err!(Config(
|
||||
"url_preview_bound_interface",
|
||||
"Not a valid IP address. Interface names not supported on {OS}."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1250,15 +1250,19 @@ pub struct Config {
|
|||
#[serde(default = "default_ip_range_denylist")]
|
||||
pub ip_range_denylist: Vec<String>,
|
||||
|
||||
/// 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.
|
||||
/// Optional IP address or network interface-name to bind as the source of
|
||||
/// URL preview requests. If not set, it will not bind to a specific
|
||||
/// address or interface.
|
||||
///
|
||||
/// To list the interfaces on your system, use the command `ip link show`
|
||||
/// Interface names only supported on Linux, Android, and Fuchsia platforms;
|
||||
/// all other platforms can specify the IP address. 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"))]
|
||||
pub url_preview_bound_interface: Option<String>,
|
||||
/// example: `"eth0"` or `"1.2.3.4"`
|
||||
///
|
||||
/// default:
|
||||
#[serde(default, with = "either::serde_untagged_optional")]
|
||||
pub url_preview_bound_interface: Option<Either<IpAddr, String>>,
|
||||
|
||||
/// Vector list of domains allowed to send requests to for URL previews.
|
||||
/// Defaults to none. Note: this is a *contains* match, not an explicit
|
||||
|
@ -1970,14 +1974,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"
|
||||
},
|
||||
self.url_preview_bound_interface
|
||||
.as_ref()
|
||||
.map(Either::as_ref)
|
||||
.map(|either| either.map_left(ToString::to_string))
|
||||
.map(Either::either_into::<String>)
|
||||
.unwrap_or_default()
|
||||
.as_str(),
|
||||
);
|
||||
line(
|
||||
"URL preview domain contains allowlist",
|
||||
|
|
|
@ -47,6 +47,7 @@ bytes.workspace = true
|
|||
conduit-core.workspace = true
|
||||
conduit-database.workspace = true
|
||||
const-str.workspace = true
|
||||
either.workspace = true
|
||||
futures.workspace = true
|
||||
hickory-resolver.workspace = true
|
||||
http.workspace = true
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use conduit::{err, implement, trace, Config, Result};
|
||||
use either::Either;
|
||||
use ipaddress::IPAddress;
|
||||
use reqwest::redirect;
|
||||
|
||||
|
@ -25,23 +26,27 @@ impl crate::Service for Service {
|
|||
let config = &args.server.config;
|
||||
let resolver = args.require::<resolver::Service>("resolver");
|
||||
|
||||
let url_preview_builder = base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.redirect(redirect::Policy::limited(3));
|
||||
let url_preview_bind_addr = config
|
||||
.url_preview_bound_interface
|
||||
.clone()
|
||||
.and_then(Either::left);
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
let url_preview_builder = if let Some(interface) = &config.url_preview_bound_interface {
|
||||
url_preview_builder.interface(interface)
|
||||
} else {
|
||||
url_preview_builder
|
||||
};
|
||||
let url_preview_bind_iface = config
|
||||
.url_preview_bound_interface
|
||||
.clone()
|
||||
.and_then(Either::right);
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
default: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.build()?,
|
||||
|
||||
url_preview: url_preview_builder.build()?,
|
||||
url_preview: base(config)
|
||||
.and_then(|builder| builder_interface(builder, url_preview_bind_iface.as_deref()))?
|
||||
.local_address(url_preview_bind_addr)
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
.build()?,
|
||||
|
||||
extern_media: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
|
@ -172,6 +177,26 @@ fn base(config: &Config) -> Result<reqwest::ClientBuilder> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
fn builder_interface(builder: reqwest::ClientBuilder, config: Option<&str>) -> Result<reqwest::ClientBuilder> {
|
||||
if let Some(iface) = config {
|
||||
Ok(builder.interface(iface))
|
||||
} else {
|
||||
Ok(builder)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
|
||||
fn builder_interface(builder: reqwest::ClientBuilder, config: Option<&str>) -> Result<reqwest::ClientBuilder> {
|
||||
use conduit::Err;
|
||||
|
||||
if let Some(iface) = config {
|
||||
Err!("Binding to network-interface {iface:?} by name is not supported on this platform.")
|
||||
} else {
|
||||
Ok(builder)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[implement(Service)]
|
||||
|
|
Loading…
Add table
Reference in a new issue