-
Notifications
You must be signed in to change notification settings - Fork 352
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
//! Rustls based connector service. | ||
//! | ||
//! See [`TlsConnector`] for main connector service factory docs. | ||
use std::{ | ||
future::Future, | ||
io, | ||
pin::Pin, | ||
sync::Arc, | ||
task::{Context, Poll}, | ||
}; | ||
|
||
use actix_rt::net::ActixStream; | ||
use actix_service::{Service, ServiceFactory}; | ||
use actix_utils::future::{ok, Ready}; | ||
use futures_core::ready; | ||
use rustls_pki_types_1::ServerName; | ||
use tokio_rustls::{ | ||
client::TlsStream as AsyncTlsStream, | ||
rustls::{ClientConfig, RootCertStore}, | ||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector, | ||
}; | ||
use tokio_rustls_025 as tokio_rustls; | ||
|
||
use crate::connect::{Connection, Host}; | ||
|
||
pub mod reexports { | ||
//! Re-exports from the `rustls` v0.22 ecosystem that are useful for connectors. | ||
pub use tokio_rustls_025::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig}; | ||
#[cfg(feature = "rustls-0_22-webpki-roots")] | ||
pub use webpki_roots_025::TLS_SERVER_ROOTS; | ||
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / Linux / msrv
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / Linux / stable
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / macOS / msrv
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / macOS / stable
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / Windows / msrv
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / Windows / stable
Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs GitHub Actions / Windows (32-bit) / msrv
|
||
} | ||
|
||
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store. | ||
/// | ||
/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors. | ||
#[cfg(feature = "rustls-0_22-native-roots")] | ||
pub fn native_roots_cert_store() -> io::Result<RootCertStore> { | ||
let mut root_certs = RootCertStore::empty(); | ||
|
||
for cert in rustls_native_certs_07::load_native_certs()? { | ||
root_certs.add(cert).unwrap(); | ||
} | ||
|
||
Ok(root_certs) | ||
} | ||
|
||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. | ||
#[cfg(feature = "rustls-0_22-webpki-roots")] | ||
pub fn webpki_roots_cert_store() -> RootCertStore { | ||
let mut root_certs = RootCertStore::empty(); | ||
root_certs.extend(webpki_roots_026::TLS_SERVER_ROOTS.to_owned()); | ||
root_certs | ||
} | ||
|
||
/// Connector service factory using `rustls`. | ||
#[derive(Clone)] | ||
pub struct TlsConnector { | ||
connector: Arc<ClientConfig>, | ||
} | ||
|
||
impl TlsConnector { | ||
/// Constructs new connector service factory from a `rustls` client configuration. | ||
pub fn new(connector: Arc<ClientConfig>) -> Self { | ||
TlsConnector { connector } | ||
} | ||
|
||
/// Constructs new connector service from a `rustls` client configuration. | ||
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService { | ||
TlsConnectorService { connector } | ||
} | ||
} | ||
|
||
impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector | ||
where | ||
R: Host, | ||
IO: ActixStream + 'static, | ||
{ | ||
type Response = Connection<R, AsyncTlsStream<IO>>; | ||
type Error = io::Error; | ||
type Config = (); | ||
type Service = TlsConnectorService; | ||
type InitError = (); | ||
type Future = Ready<Result<Self::Service, Self::InitError>>; | ||
|
||
fn new_service(&self, _: ()) -> Self::Future { | ||
ok(TlsConnectorService { | ||
connector: self.connector.clone(), | ||
}) | ||
} | ||
} | ||
|
||
/// Connector service using `rustls`. | ||
#[derive(Clone)] | ||
pub struct TlsConnectorService { | ||
connector: Arc<ClientConfig>, | ||
} | ||
|
||
impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService | ||
where | ||
R: Host, | ||
IO: ActixStream, | ||
{ | ||
type Response = Connection<R, AsyncTlsStream<IO>>; | ||
type Error = io::Error; | ||
type Future = ConnectFut<R, IO>; | ||
|
||
actix_service::always_ready!(); | ||
|
||
fn call(&self, connection: Connection<R, IO>) -> Self::Future { | ||
tracing::trace!("TLS handshake start for: {:?}", connection.hostname()); | ||
let (stream, conn) = connection.replace_io(()); | ||
|
||
match ServerName::try_from(conn.hostname()) { | ||
Ok(host) => ConnectFut::Future { | ||
connect: RustlsTlsConnector::from(Arc::clone(&self.connector)) | ||
.connect(host.to_owned(), stream), | ||
connection: Some(conn), | ||
}, | ||
Err(_) => ConnectFut::InvalidServerName, | ||
} | ||
} | ||
} | ||
|
||
/// Connect future for Rustls service. | ||
#[doc(hidden)] | ||
#[allow(clippy::large_enum_variant)] | ||
pub enum ConnectFut<R, IO> { | ||
InvalidServerName, | ||
Future { | ||
connect: RustlsConnect<IO>, | ||
connection: Option<Connection<R, ()>>, | ||
}, | ||
} | ||
|
||
impl<R, IO> Future for ConnectFut<R, IO> | ||
where | ||
R: Host, | ||
IO: ActixStream, | ||
{ | ||
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>; | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
match self.get_mut() { | ||
Self::InvalidServerName => Poll::Ready(Err(io::Error::new( | ||
io::ErrorKind::InvalidInput, | ||
"connection parameters specified invalid server name", | ||
))), | ||
|
||
Self::Future { | ||
connect, | ||
connection, | ||
} => { | ||
let stream = ready!(Pin::new(connect).poll(cx))?; | ||
let connection = connection.take().unwrap(); | ||
tracing::trace!("TLS handshake success: {:?}", connection.hostname()); | ||
Poll::Ready(Ok(connection.replace_io(stream).1)) | ||
} | ||
} | ||
} | ||
} |