Merge branch 'tls-name' into 'master'
Validate TLS certificates according to Matrix spec See merge request famedly/conduit!60next
commit
8c72909bb8
|
@ -6,6 +6,6 @@ $ sudo usermod -aG docker $USER
|
||||||
$ exec sudo su -l $USER
|
$ exec sudo su -l $USER
|
||||||
$ sudo systemctl start docker
|
$ sudo systemctl start docker
|
||||||
$ cargo install cross
|
$ cargo install cross
|
||||||
$ cross build --release --features tls_vendored --target armv7-unknown-linux-musleabihf
|
$ cross build --release --target armv7-unknown-linux-musleabihf
|
||||||
```
|
```
|
||||||
The cross-compiled binary is at target/armv7-unknown-linux-musleabihf/release/conduit
|
The cross-compiled binary is at target/armv7-unknown-linux-musleabihf/release/conduit
|
||||||
|
|
|
@ -198,6 +198,8 @@ dependencies = [
|
||||||
"rocket",
|
"rocket",
|
||||||
"ruma",
|
"ruma",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
@ -209,6 +211,7 @@ dependencies = [
|
||||||
"tracing-opentelemetry",
|
"tracing-opentelemetry",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"trust-dns-resolver",
|
"trust-dns-resolver",
|
||||||
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -420,21 +423,6 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
|
||||||
dependencies = [
|
|
||||||
"foreign-types-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types-shared"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -716,16 +704,18 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-rustls"
|
||||||
version = "0.5.0"
|
version = "0.22.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"futures-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"native-tls",
|
"log",
|
||||||
|
"rustls",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-rustls",
|
||||||
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1001,24 +991,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "native-tls"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"openssl",
|
|
||||||
"openssl-probe",
|
|
||||||
"openssl-sys",
|
|
||||||
"schannel",
|
|
||||||
"security-framework",
|
|
||||||
"security-framework-sys",
|
|
||||||
"tempfile",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -1096,49 +1068,12 @@ version = "1.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl"
|
|
||||||
version = "0.10.33"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cfg-if",
|
|
||||||
"foreign-types",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"openssl-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-src"
|
|
||||||
version = "111.15.0+1.1.1k"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-sys"
|
|
||||||
version = "0.9.61"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"openssl-src",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry"
|
name = "opentelemetry"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
@ -1280,12 +1215,6 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkg-config"
|
|
||||||
version = "0.3.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.16.8"
|
version = "0.16.8"
|
||||||
|
@ -1486,15 +1415,6 @@ version = "0.6.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "remove_dir_all"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
|
@ -1509,19 +1429,20 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-tls",
|
"hyper-rustls",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-rustls",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
@ -1886,6 +1807,18 @@ dependencies = [
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-native-certs"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
|
||||||
|
dependencies = [
|
||||||
|
"openssl-probe",
|
||||||
|
"rustls",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -2191,20 +2124,6 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempfile"
|
|
||||||
version = "3.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"rand",
|
|
||||||
"redox_syscall 0.2.5",
|
|
||||||
"remove_dir_all",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -2358,16 +2277,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-native-tls"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
|
||||||
dependencies = [
|
|
||||||
"native-tls",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
|
@ -2613,12 +2522,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vcpkg"
|
|
||||||
version = "0.2.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
|
@ -49,7 +49,11 @@ rand = "0.8.3"
|
||||||
# Used to hash passwords
|
# Used to hash passwords
|
||||||
rust-argon2 = "0.8.3"
|
rust-argon2 = "0.8.3"
|
||||||
# Used to send requests
|
# Used to send requests
|
||||||
reqwest = { version = "0.11.1" }
|
reqwest = { version = "0.11.3", default-features = false, features = ["rustls-tls-native-roots"] }
|
||||||
|
# Custom TLS verifier
|
||||||
|
rustls = { version = "0.19", features = ["dangerous_configuration"] }
|
||||||
|
rustls-native-certs = "0.5.0"
|
||||||
|
webpki = "0.21.0"
|
||||||
# Used for conduit::Error type
|
# Used for conduit::Error type
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
# Used to generate thumbnails for images
|
# Used to generate thumbnails for images
|
||||||
|
@ -75,7 +79,6 @@ pretty_env_logger = "0.4.0"
|
||||||
[features]
|
[features]
|
||||||
default = ["conduit_bin"]
|
default = ["conduit_bin"]
|
||||||
conduit_bin = [] # TODO: add rocket to this when it is optional
|
conduit_bin = [] # TODO: add rocket to this when it is optional
|
||||||
tls_vendored = ["reqwest/native-tls-vendored"]
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::{database::Config, utils, Error, Result};
|
use crate::{database::Config, utils, Error, Result};
|
||||||
use log::error;
|
use log::{error, info};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::federation::discovery::{ServerSigningKeys, VerifyKey},
|
api::federation::discovery::{ServerSigningKeys, VerifyKey},
|
||||||
ServerName, ServerSigningKeyId,
|
ServerName, ServerSigningKeyId,
|
||||||
};
|
};
|
||||||
|
use rustls::{ServerCertVerifier, WebPKIVerifier};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
|
@ -14,9 +15,11 @@ use trust_dns_resolver::TokioAsyncResolver;
|
||||||
pub const COUNTER: &str = "c";
|
pub const COUNTER: &str = "c";
|
||||||
|
|
||||||
type WellKnownMap = HashMap<Box<ServerName>, (String, String)>;
|
type WellKnownMap = HashMap<Box<ServerName>, (String, String)>;
|
||||||
|
type TlsNameMap = HashMap<String, webpki::DNSName>;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Globals {
|
pub struct Globals {
|
||||||
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
||||||
|
pub tls_name_override: Arc<RwLock<TlsNameMap>>,
|
||||||
pub(super) globals: sled::Tree,
|
pub(super) globals: sled::Tree,
|
||||||
config: Config,
|
config: Config,
|
||||||
keypair: Arc<ruma::signatures::Ed25519KeyPair>,
|
keypair: Arc<ruma::signatures::Ed25519KeyPair>,
|
||||||
|
@ -26,6 +29,36 @@ pub struct Globals {
|
||||||
pub(super) servertimeout_signingkey: sled::Tree, // ServerName + Timeout Timestamp -> algorithm:key + pubkey
|
pub(super) servertimeout_signingkey: sled::Tree, // ServerName + Timeout Timestamp -> algorithm:key + pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MatrixServerVerifier {
|
||||||
|
inner: WebPKIVerifier,
|
||||||
|
tls_name_override: Arc<RwLock<TlsNameMap>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCertVerifier for MatrixServerVerifier {
|
||||||
|
fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
roots: &rustls::RootCertStore,
|
||||||
|
presented_certs: &[rustls::Certificate],
|
||||||
|
dns_name: webpki::DNSNameRef<'_>,
|
||||||
|
ocsp_response: &[u8],
|
||||||
|
) -> std::result::Result<rustls::ServerCertVerified, rustls::TLSError> {
|
||||||
|
if let Some(override_name) = self.tls_name_override.read().unwrap().get(dns_name.into()) {
|
||||||
|
let result = self.inner.verify_server_cert(
|
||||||
|
roots,
|
||||||
|
presented_certs,
|
||||||
|
override_name.as_ref(),
|
||||||
|
ocsp_response,
|
||||||
|
);
|
||||||
|
if result.is_ok() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
info!("Server {:?} is non-compliant, retrying TLS verification with original name", dns_name);
|
||||||
|
}
|
||||||
|
self.inner
|
||||||
|
.verify_server_cert(roots, presented_certs, dns_name, ocsp_response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Globals {
|
impl Globals {
|
||||||
pub fn load(
|
pub fn load(
|
||||||
globals: sled::Tree,
|
globals: sled::Tree,
|
||||||
|
@ -66,10 +99,21 @@ impl Globals {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tls_name_override = Arc::new(RwLock::new(TlsNameMap::new()));
|
||||||
|
let verifier = Arc::new(MatrixServerVerifier {
|
||||||
|
inner: WebPKIVerifier::new(),
|
||||||
|
tls_name_override: tls_name_override.clone(),
|
||||||
|
});
|
||||||
|
let mut tlsconfig = rustls::ClientConfig::new();
|
||||||
|
tlsconfig.dangerous().set_certificate_verifier(verifier);
|
||||||
|
tlsconfig.root_store =
|
||||||
|
rustls_native_certs::load_native_certs().expect("Error loading system certificates");
|
||||||
|
|
||||||
let reqwest_client = reqwest::Client::builder()
|
let reqwest_client = reqwest::Client::builder()
|
||||||
.connect_timeout(Duration::from_secs(30))
|
.connect_timeout(Duration::from_secs(30))
|
||||||
.timeout(Duration::from_secs(60 * 3))
|
.timeout(Duration::from_secs(60 * 3))
|
||||||
.pool_max_idle_per_host(1)
|
.pool_max_idle_per_host(1)
|
||||||
|
.use_preconfigured_tls(tlsconfig)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -86,7 +130,8 @@ impl Globals {
|
||||||
dns_resolver: TokioAsyncResolver::tokio_from_system_conf().map_err(|_| {
|
dns_resolver: TokioAsyncResolver::tokio_from_system_conf().map_err(|_| {
|
||||||
Error::bad_config("Failed to set up trust dns resolver with system config.")
|
Error::bad_config("Failed to set up trust dns resolver with system config.")
|
||||||
})?,
|
})?,
|
||||||
actual_destination_cache: Arc::new(RwLock::new(HashMap::new())),
|
actual_destination_cache: Arc::new(RwLock::new(WellKnownMap::new())),
|
||||||
|
tls_name_override,
|
||||||
servertimeout_signingkey,
|
servertimeout_signingkey,
|
||||||
jwt_decoding_key,
|
jwt_decoding_key,
|
||||||
})
|
})
|
||||||
|
|
|
@ -53,6 +53,49 @@ use std::{
|
||||||
#[cfg(feature = "conduit_bin")]
|
#[cfg(feature = "conduit_bin")]
|
||||||
use rocket::{get, post, put};
|
use rocket::{get, post, put};
|
||||||
|
|
||||||
|
/// Wraps either an literal IP address plus port, or a hostname plus complement
|
||||||
|
/// (colon-plus-port if it was specified).
|
||||||
|
///
|
||||||
|
/// Note: A `FedDest::Named` might contain an IP address in string form if there
|
||||||
|
/// was no port specified to construct a SocketAddr with.
|
||||||
|
///
|
||||||
|
/// # Examples:
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// FedDest::Literal("198.51.100.3:8448".parse()?);
|
||||||
|
/// FedDest::Literal("[2001:db8::4:5]:443".parse()?);
|
||||||
|
/// FedDest::Named("matrix.example.org".to_owned(), "".to_owned());
|
||||||
|
/// FedDest::Named("matrix.example.org".to_owned(), ":8448".to_owned());
|
||||||
|
/// FedDest::Named("198.51.100.5".to_owned(), "".to_owned());
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum FedDest {
|
||||||
|
Literal(SocketAddr),
|
||||||
|
Named(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FedDest {
|
||||||
|
fn into_https_string(self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Literal(addr) => format!("https://{}", addr),
|
||||||
|
Self::Named(host, port) => format!("https://{}{}", host, port),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_uri_string(self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Literal(addr) => addr.to_string(),
|
||||||
|
Self::Named(host, ref port) => host + port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hostname(&self) -> String {
|
||||||
|
match &self {
|
||||||
|
Self::Literal(addr) => addr.ip().to_string(),
|
||||||
|
Self::Named(host, _) => host.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(globals))]
|
#[tracing::instrument(skip(globals))]
|
||||||
pub async fn send_request<T: OutgoingRequest>(
|
pub async fn send_request<T: OutgoingRequest>(
|
||||||
globals: &crate::database::globals::Globals,
|
globals: &crate::database::globals::Globals,
|
||||||
|
@ -77,12 +120,24 @@ where
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
let result = find_actual_destination(globals, &destination).await;
|
let result = find_actual_destination(globals, &destination).await;
|
||||||
|
let (actual_destination, host) = result.clone();
|
||||||
|
let result_string = (result.0.into_https_string(), result.1.into_uri_string());
|
||||||
globals
|
globals
|
||||||
.actual_destination_cache
|
.actual_destination_cache
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(Box::<ServerName>::from(destination), result.clone());
|
.insert(Box::<ServerName>::from(destination), result_string.clone());
|
||||||
result
|
let dest_hostname = actual_destination.hostname();
|
||||||
|
let host_hostname = host.hostname();
|
||||||
|
if dest_hostname != host_hostname {
|
||||||
|
globals.tls_name_override.write().unwrap().insert(
|
||||||
|
dest_hostname,
|
||||||
|
webpki::DNSNameRef::try_from_ascii_str(&host_hostname)
|
||||||
|
.unwrap()
|
||||||
|
.to_owned(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result_string
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut http_request = request
|
let mut http_request = request
|
||||||
|
@ -210,22 +265,23 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn get_ip_with_port(destination_str: String) -> Option<String> {
|
fn get_ip_with_port(destination_str: &str) -> Option<FedDest> {
|
||||||
if destination_str.parse::<SocketAddr>().is_ok() {
|
if let Ok(destination) = destination_str.parse::<SocketAddr>() {
|
||||||
Some(destination_str)
|
Some(FedDest::Literal(destination))
|
||||||
} else if let Ok(ip_addr) = destination_str.parse::<IpAddr>() {
|
} else if let Ok(ip_addr) = destination_str.parse::<IpAddr>() {
|
||||||
Some(SocketAddr::new(ip_addr, 8448).to_string())
|
Some(FedDest::Literal(SocketAddr::new(ip_addr, 8448)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn add_port_to_hostname(destination_str: String) -> String {
|
fn add_port_to_hostname(destination_str: &str) -> FedDest {
|
||||||
match destination_str.find(':') {
|
let (host, port) = match destination_str.find(':') {
|
||||||
None => destination_str.to_owned() + ":8448",
|
None => (destination_str, ":8448"),
|
||||||
Some(_) => destination_str.to_string(),
|
Some(pos) => destination_str.split_at(pos),
|
||||||
}
|
};
|
||||||
|
FedDest::Named(host.to_string(), port.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns: actual_destination, host header
|
/// Returns: actual_destination, host header
|
||||||
|
@ -235,36 +291,37 @@ fn add_port_to_hostname(destination_str: String) -> String {
|
||||||
async fn find_actual_destination(
|
async fn find_actual_destination(
|
||||||
globals: &crate::database::globals::Globals,
|
globals: &crate::database::globals::Globals,
|
||||||
destination: &'_ ServerName,
|
destination: &'_ ServerName,
|
||||||
) -> (String, String) {
|
) -> (FedDest, FedDest) {
|
||||||
let destination_str = destination.as_str().to_owned();
|
let destination_str = destination.as_str().to_owned();
|
||||||
let mut host = destination_str.clone();
|
let mut hostname = destination_str.clone();
|
||||||
let actual_destination = "https://".to_owned()
|
let actual_destination = match get_ip_with_port(&destination_str) {
|
||||||
+ &match get_ip_with_port(destination_str.clone()) {
|
|
||||||
Some(host_port) => {
|
Some(host_port) => {
|
||||||
// 1: IP literal with provided or default port
|
// 1: IP literal with provided or default port
|
||||||
host_port
|
host_port
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if destination_str.find(':').is_some() {
|
if let Some(pos) = destination_str.find(':') {
|
||||||
// 2: Hostname with included port
|
// 2: Hostname with included port
|
||||||
destination_str
|
let (host, port) = destination_str.split_at(pos);
|
||||||
|
FedDest::Named(host.to_string(), port.to_string())
|
||||||
} else {
|
} else {
|
||||||
match request_well_known(globals, &destination.as_str()).await {
|
match request_well_known(globals, &destination.as_str()).await {
|
||||||
// 3: A .well-known file is available
|
// 3: A .well-known file is available
|
||||||
Some(delegated_hostname) => {
|
Some(delegated_hostname) => {
|
||||||
host = delegated_hostname.clone();
|
hostname = delegated_hostname.clone();
|
||||||
match get_ip_with_port(delegated_hostname.clone()) {
|
match get_ip_with_port(&delegated_hostname) {
|
||||||
Some(host_and_port) => host_and_port, // 3.1: IP literal in .well-known file
|
Some(host_and_port) => host_and_port, // 3.1: IP literal in .well-known file
|
||||||
None => {
|
None => {
|
||||||
if destination_str.find(':').is_some() {
|
if let Some(pos) = destination_str.find(':') {
|
||||||
// 3.2: Hostname with port in .well-known file
|
// 3.2: Hostname with port in .well-known file
|
||||||
destination_str
|
let (host, port) = destination_str.split_at(pos);
|
||||||
|
FedDest::Named(host.to_string(), port.to_string())
|
||||||
} else {
|
} else {
|
||||||
match query_srv_record(globals, &delegated_hostname).await {
|
match query_srv_record(globals, &delegated_hostname).await {
|
||||||
// 3.3: SRV lookup successful
|
// 3.3: SRV lookup successful
|
||||||
Some(hostname) => hostname,
|
Some(hostname) => hostname,
|
||||||
// 3.4: No SRV records, just use the hostname from .well-known
|
// 3.4: No SRV records, just use the hostname from .well-known
|
||||||
None => add_port_to_hostname(delegated_hostname),
|
None => add_port_to_hostname(&delegated_hostname),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,7 +333,7 @@ async fn find_actual_destination(
|
||||||
// 4: SRV record found
|
// 4: SRV record found
|
||||||
Some(hostname) => hostname,
|
Some(hostname) => hostname,
|
||||||
// 5: No SRV record found
|
// 5: No SRV record found
|
||||||
None => add_port_to_hostname(destination_str.to_string()),
|
None => add_port_to_hostname(&destination_str),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,24 +341,39 @@ async fn find_actual_destination(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(actual_destination, host)
|
// Can't use get_ip_with_port here because we don't want to add a port
|
||||||
|
// to an IP address if it wasn't specified
|
||||||
|
let hostname = if let Ok(addr) = hostname.parse::<SocketAddr>() {
|
||||||
|
FedDest::Literal(addr)
|
||||||
|
} else if let Ok(addr) = hostname.parse::<IpAddr>() {
|
||||||
|
FedDest::Named(addr.to_string(), "".to_string())
|
||||||
|
} else if let Some(pos) = hostname.find(':') {
|
||||||
|
let (host, port) = hostname.split_at(pos);
|
||||||
|
FedDest::Named(host.to_string(), port.to_string())
|
||||||
|
} else {
|
||||||
|
FedDest::Named(hostname, "".to_string())
|
||||||
|
};
|
||||||
|
(actual_destination, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(globals))]
|
#[tracing::instrument(skip(globals))]
|
||||||
async fn query_srv_record(
|
async fn query_srv_record(
|
||||||
globals: &crate::database::globals::Globals,
|
globals: &crate::database::globals::Globals,
|
||||||
hostname: &'_ str,
|
hostname: &'_ str,
|
||||||
) -> Option<String> {
|
) -> Option<FedDest> {
|
||||||
if let Ok(Some(host_port)) = globals
|
if let Ok(Some(host_port)) = globals
|
||||||
.dns_resolver()
|
.dns_resolver()
|
||||||
.srv_lookup(format!("_matrix._tcp.{}", hostname))
|
.srv_lookup(format!("_matrix._tcp.{}", hostname))
|
||||||
.await
|
.await
|
||||||
.map(|srv| {
|
.map(|srv| {
|
||||||
srv.iter().next().map(|result| {
|
srv.iter().next().map(|result| {
|
||||||
format!(
|
FedDest::Named(
|
||||||
"{}:{}",
|
result
|
||||||
result.target().to_string().trim_end_matches('.'),
|
.target()
|
||||||
result.port().to_string()
|
.to_string()
|
||||||
|
.trim_end_matches('.')
|
||||||
|
.to_string(),
|
||||||
|
format!(":{}", result.port()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -2133,45 +2205,45 @@ pub async fn fetch_required_signing_keys(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{add_port_to_hostname, get_ip_with_port};
|
use super::{FedDest, add_port_to_hostname, get_ip_with_port};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ips_get_default_ports() {
|
fn ips_get_default_ports() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_ip_with_port(String::from("1.1.1.1")),
|
get_ip_with_port("1.1.1.1"),
|
||||||
Some(String::from("1.1.1.1:8448"))
|
Some(FedDest::Literal("1.1.1.1:8448".parse().unwrap()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_ip_with_port(String::from("dead:beef::")),
|
get_ip_with_port("dead:beef::"),
|
||||||
Some(String::from("[dead:beef::]:8448"))
|
Some(FedDest::Literal("[dead:beef::]:8448".parse().unwrap()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ips_keep_custom_ports() {
|
fn ips_keep_custom_ports() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_ip_with_port(String::from("1.1.1.1:1234")),
|
get_ip_with_port("1.1.1.1:1234"),
|
||||||
Some(String::from("1.1.1.1:1234"))
|
Some(FedDest::Literal("1.1.1.1:1234".parse().unwrap()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_ip_with_port(String::from("[dead::beef]:8933")),
|
get_ip_with_port("[dead::beef]:8933"),
|
||||||
Some(String::from("[dead::beef]:8933"))
|
Some(FedDest::Literal("[dead::beef]:8933".parse().unwrap()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hostnames_get_default_ports() {
|
fn hostnames_get_default_ports() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
add_port_to_hostname(String::from("example.com")),
|
add_port_to_hostname("example.com"),
|
||||||
"example.com:8448"
|
FedDest::Named(String::from("example.com"), String::from(":8448"))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hostnames_keep_custom_ports() {
|
fn hostnames_keep_custom_ports() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
add_port_to_hostname(String::from("example.com:1337")),
|
add_port_to_hostname("example.com:1337"),
|
||||||
"example.com:1337"
|
FedDest::Named(String::from("example.com"), String::from(":1337"))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue