mirror of https://github.com/fafhrd91/actix-net
164 lines
4.9 KiB
Rust
164 lines
4.9 KiB
Rust
//! 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, 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_026::TLS_SERVER_ROOTS;
|
|
}
|
|
|
|
/// 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.
|
|
///
|
|
/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_07::load_native_certs()
|
|
#[cfg(feature = "rustls-0_22-native-roots")]
|
|
pub fn native_roots_cert_store() -> io::Result<tokio_rustls::rustls::RootCertStore> {
|
|
let mut root_certs = tokio_rustls::rustls::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() -> tokio_rustls::rustls::RootCertStore {
|
|
let mut root_certs = tokio_rustls::rustls::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))
|
|
}
|
|
}
|
|
}
|
|
}
|