Skip to content

Commit 78a0d75

Browse files
committed
feat: use Rustls instead of native TLS for HTTPS requests
HTTPS requests are used to fetch remote images in HTML emails, to fetch autoconfig XML, to POST requests for `DCACCOUNT:` QR codes to make OAuth 2 API requests and to connect to HTTPS proxies. Rustls is more aggressive than OpenSSL in deprecating cryptographic algorithms so we cannot use it for IMAP and SMTP to avoid breaking compatibility, but for HTTPS requests listed above this should not result in problems. As HTTPS requests use only strict TLS checks, there is no `strict_tls` argument in `wrap_rustls` function. Rustls is already used by iroh, so this change does not introduce new dependencies.
1 parent 638da90 commit 78a0d75

File tree

6 files changed

+30
-19
lines changed

6 files changed

+30
-19
lines changed

Cargo.lock

Lines changed: 12 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ rand = { workspace = true }
8484
regex = { workspace = true }
8585
rusqlite = { workspace = true, features = ["sqlcipher"] }
8686
rust-hsluv = "0.1"
87+
rustls-pki-types = "1.8.0"
88+
rustls = { version = "0.23.13", default-features = false }
8789
sanitize-filename = { workspace = true }
8890
serde_json = { workspace = true }
8991
serde_urlencoded = "0.7.1"
@@ -97,13 +99,15 @@ tagger = "4.3.4"
9799
textwrap = "0.16.1"
98100
thiserror = { workspace = true }
99101
tokio-io-timeout = "1.2.0"
102+
tokio-rustls = { version = "0.26.0", default-features = false }
100103
tokio-stream = { version = "0.1.15", features = ["fs"] }
101104
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
102105
tokio-util = { workspace = true }
103106
tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] }
104107
toml = "0.8"
105108
url = "2"
106109
uuid = { version = "1", features = ["serde", "v4"] }
110+
webpki-roots = "0.26.6"
107111

108112
[dev-dependencies]
109113
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.
@@ -197,7 +201,6 @@ yerpc = "0.6.2"
197201
default = ["vendored"]
198202
internals = []
199203
vendored = [
200-
"async-native-tls/vendored",
201204
"rusqlite/bundled-sqlcipher-vendored-openssl"
202205
]
203206

src/net/http.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use serde::Serialize;
1010
use crate::context::Context;
1111
use crate::net::proxy::ProxyConfig;
1212
use crate::net::session::SessionStream;
13-
use crate::net::tls::wrap_tls;
13+
use crate::net::tls::wrap_rustls;
1414

1515
/// HTTP(S) GET response.
1616
#[derive(Debug)]
@@ -67,17 +67,16 @@ where
6767
"https" => {
6868
let port = parsed_url.port_u16().unwrap_or(443);
6969
let load_cache = true;
70-
let strict_tls = true;
7170

7271
if let Some(proxy_config) = proxy_config_opt {
7372
let proxy_stream = proxy_config
7473
.connect(context, host, port, load_cache)
7574
.await?;
76-
let tls_stream = wrap_tls(strict_tls, host, &[], proxy_stream).await?;
75+
let tls_stream = wrap_rustls(host, &[], proxy_stream).await?;
7776
Box::new(tls_stream)
7877
} else {
7978
let tcp_stream = crate::net::connect_tcp(context, host, port, load_cache).await?;
80-
let tls_stream = wrap_tls(strict_tls, host, &[], tcp_stream).await?;
79+
let tls_stream = wrap_rustls(host, &[], tcp_stream).await?;
8180
Box::new(tls_stream)
8281
}
8382
}

src/net/proxy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ use url::Url;
2020

2121
use crate::config::Config;
2222
use crate::context::Context;
23+
use crate::net::connect_tcp;
2324
use crate::net::session::SessionStream;
24-
use crate::net::{connect_tcp, wrap_tls};
25+
use crate::net::tls::wrap_rustls;
2526
use crate::sql::Sql;
2627

2728
/// Default SOCKS5 port according to [RFC 1928](https://tools.ietf.org/html/rfc1928).
@@ -375,15 +376,14 @@ impl ProxyConfig {
375376
}
376377
ProxyConfig::Https(https_config) => {
377378
let load_cache = true;
378-
let strict_tls = true;
379379
let tcp_stream = crate::net::connect_tcp(
380380
context,
381381
&https_config.host,
382382
https_config.port,
383383
load_cache,
384384
)
385385
.await?;
386-
let tls_stream = wrap_tls(strict_tls, &https_config.host, &[], tcp_stream).await?;
386+
let tls_stream = wrap_rustls(&https_config.host, &[], tcp_stream).await?;
387387
let auth = if let Some((username, password)) = &https_config.user_password {
388388
Some((username.as_str(), password.as_str()))
389389
} else {

src/net/session.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use async_native_tls::TlsStream;
21
use fast_socks5::client::Socks5Stream;
32
use std::pin::Pin;
43
use std::time::Duration;
@@ -17,11 +16,16 @@ impl SessionStream for Box<dyn SessionStream> {
1716
self.as_mut().set_read_timeout(timeout);
1817
}
1918
}
20-
impl<T: SessionStream> SessionStream for TlsStream<T> {
19+
impl<T: SessionStream> SessionStream for async_native_tls::TlsStream<T> {
2120
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
2221
self.get_mut().set_read_timeout(timeout);
2322
}
2423
}
24+
impl<T: SessionStream> SessionStream for tokio_rustls::client::TlsStream<T> {
25+
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
26+
self.get_mut().0.set_read_timeout(timeout);
27+
}
28+
}
2529
impl<T: SessionStream> SessionStream for BufStream<T> {
2630
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
2731
self.get_mut().set_read_timeout(timeout);

src/net/tls.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! TLS support.
2+
use std::sync::Arc;
23

34
use anyhow::Result;
45
use async_native_tls::{Certificate, Protocol, TlsConnector, TlsStream};
@@ -46,7 +47,7 @@ pub async fn wrap_rustls<T: AsyncRead + AsyncWrite + Unpin>(
4647
let mut config = rustls::ClientConfig::builder()
4748
.with_root_certificates(root_cert_store)
4849
.with_no_client_auth();
49-
config.alpn_protocols = alpn.into_iter().map(|s| s.as_bytes().to_vec()).collect();
50+
config.alpn_protocols = alpn.iter().map(|s| s.as_bytes().to_vec()).collect();
5051

5152
let tls = tokio_rustls::TlsConnector::from(Arc::new(config));
5253
let name = rustls_pki_types::ServerName::try_from(hostname)?.to_owned();

0 commit comments

Comments
 (0)