#![doc(html_root_url = "https://docs.rs/tokio-tls/0.2.1")] #![deny(rust_2018_idioms)] #![cfg_attr(test, deny(warnings))] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] //! Async TLS streams //! //! This library is an implementation of TLS streams using the most appropriate //! system library by default for negotiating the connection. That is, on //! Windows this library uses SChannel, on OSX it uses SecureTransport, and on //! other platforms it uses OpenSSL. //! //! Each TLS stream implements the `Read` and `Write` traits to interact and //! interoperate with the rest of the futures I/O ecosystem. Client connections //! initiated from this crate verify hostnames automatically and by default. //! //! This crate primarily exports this ability through two newtypes, //! `TlsConnector` and `TlsAcceptor`. These newtypes augment the //! functionality provided by the `native-tls` crate, on which this crate is //! built. Configuration of TLS parameters is still primarily done through the //! `native-tls` crate. use futures::{Async, Future, Poll}; use native_tls::{Error, HandshakeError}; use std::io::{self, Read, Write}; use tokio_io::{try_nb, AsyncRead, AsyncWrite}; /// A wrapper around an underlying raw stream which implements the TLS or SSL /// protocol. /// /// A `TlsStream` represents a handshake that has been completed successfully /// and both the server and the client are ready for receiving and sending /// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written /// to a `TlsStream` are encrypted when passing through to `S`. #[derive(Debug)] pub struct TlsStream { inner: native_tls::TlsStream, } /// A wrapper around a `native_tls::TlsConnector`, providing an async `connect` /// method. #[derive(Clone)] pub struct TlsConnector { inner: native_tls::TlsConnector, } /// A wrapper around a `native_tls::TlsAcceptor`, providing an async `accept` /// method. #[derive(Clone)] pub struct TlsAcceptor { inner: native_tls::TlsAcceptor, } /// Future returned from `TlsConnector::connect` which will resolve /// once the connection handshake has finished. pub struct Connect { inner: MidHandshake, } /// Future returned from `TlsAcceptor::accept` which will resolve /// once the accept handshake has finished. pub struct Accept { inner: MidHandshake, } struct MidHandshake { inner: Option, HandshakeError>>, } impl TlsStream { /// Get access to the internal `native_tls::TlsStream` stream which also /// transitively allows access to `S`. pub fn get_ref(&self) -> &native_tls::TlsStream { &self.inner } /// Get mutable access to the internal `native_tls::TlsStream` stream which /// also transitively allows mutable access to `S`. pub fn get_mut(&mut self) -> &mut native_tls::TlsStream { &mut self.inner } } impl Read for TlsStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } } impl Write for TlsStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } impl AsyncRead for TlsStream {} impl AsyncWrite for TlsStream { fn shutdown(&mut self) -> Poll<(), io::Error> { try_nb!(self.inner.shutdown()); self.inner.get_mut().shutdown() } } impl TlsConnector { /// Connects the provided stream with this connector, assuming the provided /// domain. /// /// This function will internally call `TlsConnector::connect` to connect /// the stream and returns a future representing the resolution of the /// connection operation. The returned future will resolve to either /// `TlsStream` or `Error` depending if it's successful or not. /// /// This is typically used for clients who have already established, for /// example, a TCP connection to a remote server. That stream is then /// provided here to perform the client half of a connection to a /// TLS-powered server. pub fn connect(&self, domain: &str, stream: S) -> Connect where S: AsyncRead + AsyncWrite, { Connect { inner: MidHandshake { inner: Some(self.inner.connect(domain, stream)), }, } } } impl From for TlsConnector { fn from(inner: native_tls::TlsConnector) -> TlsConnector { TlsConnector { inner } } } impl TlsAcceptor { /// Accepts a new client connection with the provided stream. /// /// This function will internally call `TlsAcceptor::accept` to connect /// the stream and returns a future representing the resolution of the /// connection operation. The returned future will resolve to either /// `TlsStream` or `Error` depending if it's successful or not. /// /// This is typically used after a new socket has been accepted from a /// `TcpListener`. That socket is then passed to this function to perform /// the server half of accepting a client connection. pub fn accept(&self, stream: S) -> Accept where S: AsyncRead + AsyncWrite, { Accept { inner: MidHandshake { inner: Some(self.inner.accept(stream)), }, } } } impl From for TlsAcceptor { fn from(inner: native_tls::TlsAcceptor) -> TlsAcceptor { TlsAcceptor { inner } } } impl Future for Connect { type Item = TlsStream; type Error = Error; fn poll(&mut self) -> Poll, Error> { self.inner.poll() } } impl Future for Accept { type Item = TlsStream; type Error = Error; fn poll(&mut self) -> Poll, Error> { self.inner.poll() } } impl Future for MidHandshake { type Item = TlsStream; type Error = Error; fn poll(&mut self) -> Poll, Error> { match self.inner.take().expect("cannot poll MidHandshake twice") { Ok(stream) => Ok(TlsStream { inner: stream }.into()), Err(HandshakeError::Failure(e)) => Err(e), Err(HandshakeError::WouldBlock(s)) => match s.handshake() { Ok(stream) => Ok(TlsStream { inner: stream }.into()), Err(HandshakeError::Failure(e)) => Err(e), Err(HandshakeError::WouldBlock(s)) => { self.inner = Some(Err(HandshakeError::WouldBlock(s))); Ok(Async::NotReady) } }, } } }