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))
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |