use std::fmt;
use std::io;
use std::sync::Arc;
use rustls::pki_types::{CertificateDer, ServerName};
use rustls::{ClientConfig, ClientConnection, Connection, HandshakeKind, ProtocolVersion, SupportedCipherSuite};
use crate::buf::{BufResult, IoBuf, IoBufMut, IoVectoredBuf, IoVectoredBufMut};
use crate::io::{AsyncRead, AsyncWrite, IntoOwnedSplit, ReuniteError, ReuniteOwned};
use super::engine::Engine;
use super::split;
#[derive(Clone)]
pub struct TlsConnector {
config: Arc<ClientConfig>,
}
impl TlsConnector {
pub fn new(config: Arc<ClientConfig>) -> Self {
Self { config }
}
pub fn config(&self) -> &Arc<ClientConfig> {
&self.config
}
pub async fn connect<S>(&self, server_name: ServerName<'static>, stream: S) -> io::Result<TlsStream<S>>
where
S: AsyncRead + AsyncWrite,
{
let connection = ClientConnection::new(self.config.clone(), server_name)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
Self::connect_connection(connection, stream).await
}
pub async fn connect_with_alpn<S>(
&self,
server_name: ServerName<'static>,
stream: S,
alpn_protocols: Vec<Vec<u8>>,
) -> io::Result<TlsStream<S>>
where
S: AsyncRead + AsyncWrite,
{
let connection = ClientConnection::new_with_alpn(self.config.clone(), server_name, alpn_protocols)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
Self::connect_connection(connection, stream).await
}
async fn connect_connection<S>(connection: ClientConnection, stream: S) -> io::Result<TlsStream<S>>
where
S: AsyncRead + AsyncWrite,
{
let engine = Engine::new(stream, Connection::Client(connection)).handshake().await?;
Ok(TlsStream { engine })
}
}
impl From<Arc<ClientConfig>> for TlsConnector {
#[inline]
fn from(config: Arc<ClientConfig>) -> Self {
Self::new(config)
}
}
impl fmt::Debug for TlsConnector {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("TlsConnector").finish_non_exhaustive()
}
}
pub struct TlsStream<S> {
engine: Engine<S>,
}
impl<S> TlsStream<S> {
pub fn get_ref(&self) -> (&S, &ClientConnection) {
let (stream, connection) = self.engine.get_ref();
match connection {
Connection::Client(connection) => (stream, connection),
Connection::Server(_) => unreachable!("client TLS stream contains a server connection"),
}
}
pub fn alpn_protocol(&self) -> Option<&[u8]> {
self.get_ref().1.alpn_protocol()
}
pub fn peer_certificates(&self) -> Option<&[CertificateDer<'static>]> {
self.get_ref().1.peer_certificates()
}
pub fn protocol_version(&self) -> Option<ProtocolVersion> {
self.get_ref().1.protocol_version()
}
pub fn negotiated_cipher_suite(&self) -> Option<SupportedCipherSuite> {
self.get_ref().1.negotiated_cipher_suite()
}
pub fn handshake_kind(&self) -> Option<HandshakeKind> {
self.get_ref().1.handshake_kind()
}
pub fn export_keying_material<T: AsMut<[u8]>>(
&self,
output: T,
label: &[u8],
context: Option<&[u8]>,
) -> Result<T, rustls::Error> {
self.get_ref().1.export_keying_material(output, label, context)
}
pub fn into_parts(self) -> io::Result<(S, ClientConnection)> {
let (stream, connection) = self.engine.into_parts()?;
match connection {
Connection::Client(connection) => Ok((stream, connection)),
Connection::Server(_) => unreachable!("client TLS stream contains a server connection"),
}
}
}
impl<S: IntoOwnedSplit + 'static> TlsStream<S> {
pub fn into_split(self) -> (OwnedReadHalf<S>, OwnedWriteHalf<S>) {
<Self as IntoOwnedSplit>::into_split(self)
}
}
pub struct OwnedReadHalf<S: IntoOwnedSplit> {
inner: split::ReadHalf<S::ReadHalf>,
}
impl<S: IntoOwnedSplit> fmt::Debug for OwnedReadHalf<S> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("ClientTlsReadHalf").finish_non_exhaustive()
}
}
impl<S: IntoOwnedSplit> AsyncRead for OwnedReadHalf<S> {
#[inline]
async fn read<B: IoBufMut>(&mut self, buffer: B) -> BufResult<usize, B> {
self.inner.read(buffer).await
}
#[inline]
async fn read_vectored<V: IoVectoredBufMut>(&mut self, buffers: V) -> BufResult<usize, V> {
self.inner.read_vectored(buffers).await
}
}
impl<S: ReuniteOwned> OwnedReadHalf<S> {
pub fn is_pair_of(&self, other: &OwnedWriteHalf<S>) -> bool {
split::is_pair_of(&self.inner, &other.inner)
}
pub fn reunite(self, other: OwnedWriteHalf<S>) -> Result<TlsStream<S>, ReuniteError<Self, OwnedWriteHalf<S>>> {
split::reunite::<S>(self.inner, other.inner)
.map(|engine| TlsStream { engine })
.map_err(|error| error.map_halves(|inner| Self { inner }, |inner| OwnedWriteHalf { inner }))
}
}
pub struct OwnedWriteHalf<S: IntoOwnedSplit> {
inner: split::WriteHalf<S::WriteHalf>,
}
impl<S: IntoOwnedSplit> fmt::Debug for OwnedWriteHalf<S> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("ClientTlsWriteHalf").finish_non_exhaustive()
}
}
impl<S: IntoOwnedSplit> AsyncWrite for OwnedWriteHalf<S> {
#[inline]
async fn write<B: IoBuf>(&mut self, buffer: B) -> BufResult<usize, B> {
self.inner.write(buffer).await
}
#[inline]
async fn write_vectored<V: IoVectoredBuf>(&mut self, buffers: V) -> BufResult<usize, V> {
self.inner.write_vectored(buffers).await
}
#[inline]
async fn flush(&mut self) -> io::Result<()> {
self.inner.flush().await
}
#[inline]
async fn shutdown(&mut self) -> io::Result<()> {
self.inner.shutdown().await
}
}
impl<S: ReuniteOwned> OwnedWriteHalf<S> {
pub fn is_pair_of(&self, other: &OwnedReadHalf<S>) -> bool {
other.is_pair_of(self)
}
pub fn reunite(self, other: OwnedReadHalf<S>) -> Result<TlsStream<S>, ReuniteError<OwnedReadHalf<S>, Self>> {
other.reunite(self)
}
}
impl<S: IntoOwnedSplit + 'static> IntoOwnedSplit for TlsStream<S> {
type ReadHalf = OwnedReadHalf<S>;
type WriteHalf = OwnedWriteHalf<S>;
fn into_split(self) -> (Self::ReadHalf, Self::WriteHalf) {
let (read, write) = split::split(self.engine);
(OwnedReadHalf { inner: read }, OwnedWriteHalf { inner: write })
}
}
impl<S: ReuniteOwned + 'static> ReuniteOwned for TlsStream<S> {
fn reunite(
read: Self::ReadHalf,
write: Self::WriteHalf,
) -> Result<Self, ReuniteError<Self::ReadHalf, Self::WriteHalf>> {
read.reunite(write)
}
}
impl<S> fmt::Debug for TlsStream<S> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_struct("ClientTlsStream").finish_non_exhaustive()
}
}
impl<S: AsyncRead + AsyncWrite> AsyncRead for TlsStream<S> {
#[inline]
async fn read<B: IoBufMut>(&mut self, buffer: B) -> BufResult<usize, B> {
self.engine.read(buffer).await
}
#[inline]
async fn read_vectored<V: IoVectoredBufMut>(&mut self, buffers: V) -> BufResult<usize, V> {
self.engine.read_vectored(buffers).await
}
}
impl<S: AsyncRead + AsyncWrite> AsyncWrite for TlsStream<S> {
#[inline]
async fn write<B: IoBuf>(&mut self, buffer: B) -> BufResult<usize, B> {
self.engine.write(buffer).await
}
#[inline]
async fn write_vectored<V: IoVectoredBuf>(&mut self, buffers: V) -> BufResult<usize, V> {
self.engine.write_vectored(buffers).await
}
#[inline]
async fn flush(&mut self) -> io::Result<()> {
self.engine.flush().await
}
#[inline]
async fn shutdown(&mut self) -> io::Result<()> {
self.engine.shutdown().await
}
}