Skip to content

Commit

Permalink
feat: use Rustls instead of native TLS for HTTPS requests
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
link2xt committed Sep 22, 2024
1 parent 8df2f43 commit e3eed4d
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 19 deletions.
20 changes: 12 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ rand = { workspace = true }
regex = { workspace = true }
rusqlite = { workspace = true, features = ["sqlcipher"] }
rust-hsluv = "0.1"
rustls-pki-types = "1.8.0"
rustls = { version = "0.23.13", default-features = false }
sanitize-filename = { workspace = true }
serde_json = { workspace = true }
serde_urlencoded = "0.7.1"
Expand All @@ -99,12 +101,14 @@ textwrap = "0.16.1"
thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] }
tokio-io-timeout = "1.2.0"
tokio-rustls = { version = "0.26.0", default-features = false }
tokio-stream = { version = "0.1.15", features = ["fs"] }
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
tokio-util = { workspace = true }
toml = "0.8"
url = "2"
uuid = { version = "1", features = ["serde", "v4"] }
webpki-roots = "0.26.6"

[dev-dependencies]
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.
Expand Down Expand Up @@ -198,7 +202,6 @@ yerpc = "0.6.2"
default = ["vendored"]
internals = []
vendored = [
"async-native-tls/vendored",
"rusqlite/bundled-sqlcipher-vendored-openssl"
]

Expand Down
7 changes: 3 additions & 4 deletions src/net/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use serde::Serialize;
use crate::context::Context;
use crate::net::proxy::ProxyConfig;
use crate::net::session::SessionStream;
use crate::net::tls::wrap_tls;
use crate::net::tls::wrap_rustls;

/// HTTP(S) GET response.
#[derive(Debug)]
Expand Down Expand Up @@ -67,17 +67,16 @@ where
"https" => {
let port = parsed_url.port_u16().unwrap_or(443);
let load_cache = true;
let strict_tls = true;

if let Some(proxy_config) = proxy_config_opt {
let proxy_stream = proxy_config
.connect(context, host, port, load_cache)
.await?;
let tls_stream = wrap_tls(strict_tls, host, &[], proxy_stream).await?;
let tls_stream = wrap_rustls(host, &[], proxy_stream).await?;
Box::new(tls_stream)
} else {
let tcp_stream = crate::net::connect_tcp(context, host, port, load_cache).await?;
let tls_stream = wrap_tls(strict_tls, host, &[], tcp_stream).await?;
let tls_stream = wrap_rustls(host, &[], tcp_stream).await?;
Box::new(tls_stream)
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/net/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ use url::Url;

use crate::config::Config;
use crate::context::Context;
use crate::net::connect_tcp;
use crate::net::session::SessionStream;
use crate::net::{connect_tcp, wrap_tls};
use crate::net::tls::wrap_rustls;
use crate::sql::Sql;

/// Default SOCKS5 port according to [RFC 1928](https://tools.ietf.org/html/rfc1928).
Expand Down Expand Up @@ -432,15 +433,14 @@ impl ProxyConfig {
}
ProxyConfig::Https(https_config) => {
let load_cache = true;
let strict_tls = true;
let tcp_stream = crate::net::connect_tcp(
context,
&https_config.host,
https_config.port,
load_cache,
)
.await?;
let tls_stream = wrap_tls(strict_tls, &https_config.host, &[], tcp_stream).await?;
let tls_stream = wrap_rustls(&https_config.host, &[], tcp_stream).await?;
let auth = if let Some((username, password)) = &https_config.user_password {
Some((username.as_str(), password.as_str()))
} else {
Expand Down
8 changes: 6 additions & 2 deletions src/net/session.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::net::proxy::ShadowsocksStream;
use async_native_tls::TlsStream;
use fast_socks5::client::Socks5Stream;
use std::pin::Pin;
use std::time::Duration;
Expand All @@ -18,11 +17,16 @@ impl SessionStream for Box<dyn SessionStream> {
self.as_mut().set_read_timeout(timeout);
}
}
impl<T: SessionStream> SessionStream for TlsStream<T> {
impl<T: SessionStream> SessionStream for async_native_tls::TlsStream<T> {
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
self.get_mut().set_read_timeout(timeout);
}
}
impl<T: SessionStream> SessionStream for tokio_rustls::client::TlsStream<T> {
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
self.get_mut().0.set_read_timeout(timeout);
}
}
impl<T: SessionStream> SessionStream for BufStream<T> {
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
self.get_mut().set_read_timeout(timeout);
Expand Down
3 changes: 2 additions & 1 deletion src/net/tls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! TLS support.
use std::sync::Arc;

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

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

0 comments on commit e3eed4d

Please sign in to comment.