use std::io;
use crate::error::Result;
use crate::io::runtime::AsyncConn;
use crate::proto::tls::TlsEngine;
use crate::tls::{ClientEngine, TlsSettings};
fn to_io(e: crate::error::Error) -> io::Error {
io::Error::other(e.to_string())
}
pub(crate) async fn handshake<C: AsyncConn>(
conn: &mut C,
host: &str,
tls: &TlsSettings,
) -> Result<ClientEngine> {
let mut opts = tls.to_opts(&[])?;
let pins = opts.pinned_spki_sha256.clone();
let mut engine = crate::tls::build_client_engine(host, &mut opts)?;
let mut inbuf = vec![0u8; 16 * 1024];
loop {
let mut out = Vec::new();
engine.drain_outgoing(&mut out);
if !out.is_empty() {
conn.write_all(&out).await?;
conn.flush().await?;
}
if !engine.is_handshaking() {
break;
}
let n = conn.read(&mut inbuf).await?;
if n == 0 {
return Err(crate::error::Error::UnexpectedEof);
}
engine.feed_incoming(&inbuf[..n])?;
}
tls.verify_peer(&engine.tls_params().peer_certificates, host, &pins)?;
Ok(engine)
}
pub(crate) struct AsyncTlsStream<C> {
conn: C,
engine: ClientEngine,
inbuf: Vec<u8>,
poisoned: bool,
}
impl<C: AsyncConn> AsyncTlsStream<C> {
pub(crate) async fn connect(
mut conn: C,
host: &str,
tls: &TlsSettings,
) -> Result<AsyncTlsStream<C>> {
let engine = handshake(&mut conn, host, tls).await?;
Ok(AsyncTlsStream {
conn,
engine,
inbuf: vec![0u8; 16 * 1024],
poisoned: false,
})
}
fn check_not_poisoned(&self) -> io::Result<()> {
if self.poisoned {
return Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"tls: stream unusable after an interrupted or failed write",
));
}
Ok(())
}
async fn send_queued(&mut self, flush: bool) -> io::Result<()> {
let mut out = Vec::new();
self.engine.drain_outgoing(&mut out);
if out.is_empty() && !flush {
return Ok(());
}
self.poisoned = true;
if !out.is_empty() {
self.conn.write_all(&out).await?;
}
if flush {
self.conn.flush().await?;
}
self.poisoned = false;
Ok(())
}
}
impl<C: AsyncConn> AsyncConn for AsyncTlsStream<C> {
async fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
self.check_not_poisoned()?;
loop {
let n = self.engine.read_plaintext(dst).map_err(to_io)?;
if n > 0 {
return Ok(n);
}
let m = self.conn.read(&mut self.inbuf).await?;
if m == 0 {
return Ok(0); }
let chunk = self.inbuf[..m].to_vec();
self.engine.feed_incoming(&chunk).map_err(to_io)?;
self.send_queued(true).await?;
}
}
async fn write_all(&mut self, src: &[u8]) -> io::Result<()> {
self.check_not_poisoned()?;
self.engine.write_plaintext(src);
self.send_queued(false).await
}
async fn flush(&mut self) -> io::Result<()> {
self.check_not_poisoned()?;
self.send_queued(true).await
}
}