use std::{ convert::TryFrom, future::Future, io, pin::Pin, sync::Arc, task::{Context, Poll}, }; pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig}; pub use webpki_roots::TLS_SERVER_ROOTS; use actix_rt::net::ActixStream; use actix_service::{Service, ServiceFactory}; use futures_core::{future::LocalBoxFuture, ready}; use log::trace; use tokio_rustls::rustls::{client::ServerName, OwnedTrustAnchor, RootCertStore}; use tokio_rustls::{Connect, TlsConnector}; use crate::connect::{Address, Connection}; /// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. pub fn webpki_roots_cert_store() -> RootCertStore { let mut root_certs = RootCertStore::empty(); for cert in TLS_SERVER_ROOTS.0 { let cert = OwnedTrustAnchor::from_subject_spki_name_constraints( cert.subject, cert.spki, cert.name_constraints, ); let certs = vec![cert].into_iter(); root_certs.add_server_trust_anchors(certs); } root_certs } /// Rustls connector factory pub struct RustlsConnector { connector: Arc, } impl RustlsConnector { pub fn new(connector: Arc) -> Self { RustlsConnector { connector } } } impl RustlsConnector { pub fn service(connector: Arc) -> RustlsConnectorService { RustlsConnectorService { connector } } } impl Clone for RustlsConnector { fn clone(&self) -> Self { Self { connector: self.connector.clone(), } } } impl ServiceFactory> for RustlsConnector where T: Address, U: ActixStream + 'static, { type Response = Connection>; type Error = io::Error; type Config = (); type Service = RustlsConnectorService; type InitError = (); type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { let connector = self.connector.clone(); Box::pin(async { Ok(RustlsConnectorService { connector }) }) } } pub struct RustlsConnectorService { connector: Arc, } impl Clone for RustlsConnectorService { fn clone(&self) -> Self { Self { connector: self.connector.clone(), } } } impl Service> for RustlsConnectorService where T: Address, U: ActixStream, { type Response = Connection>; type Error = io::Error; type Future = RustlsConnectorServiceFuture; actix_service::always_ready!(); fn call(&self, connection: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", connection.host()); let (stream, connection) = connection.replace_io(()); match ServerName::try_from(connection.host()) { Ok(host) => RustlsConnectorServiceFuture::Future { connect: TlsConnector::from(self.connector.clone()).connect(host, stream), connection: Some(connection), }, Err(_) => RustlsConnectorServiceFuture::InvalidDns, } } } pub enum RustlsConnectorServiceFuture { /// See issue https://github.com/briansmith/webpki/issues/54 InvalidDns, Future { connect: Connect, connection: Option>, }, } impl Future for RustlsConnectorServiceFuture where T: Address, U: ActixStream, { type Output = Result>, io::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.get_mut() { Self::InvalidDns => Poll::Ready(Err( io::Error::new(io::ErrorKind::Other, "rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54") )), Self::Future { connect, connection } => { let stream = ready!(Pin::new(connect).poll(cx))?; let connection = connection.take().unwrap(); trace!("SSL Handshake success: {:?}", connection.host()); Poll::Ready(Ok(connection.replace_io(stream).1)) } } } }