use std::{
cell::{Cell, RefCell},
future::Future,
io::{self, Read, Write},
pin::Pin,
rc::Rc,
sync::Arc,
};
pub use rustls;
use rustls::{
pki_types::ServerName, ClientConfig, ClientConnection, Connection, ServerConfig,
ServerConnection,
};
use crate::{
buf::{BufResult, IoBuf, IoBufMut},
compat::{sealed, Duplex},
};
const READ_CHUNK: usize = 16 * 1024;
fn tls_err(e: rustls::Error) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, e)
}
pub struct TlsStream<S: Duplex> {
io: Rc<S>,
conn: RefCell<Connection>,
eof: Cell<bool>,
out_spare: Cell<Vec<u8>>,
read_spare: Cell<Vec<u8>>,
}
impl<S: Duplex> TlsStream<S> {
fn new(io: S, conn: Connection) -> Self {
Self {
io: Rc::new(io),
conn: RefCell::new(conn),
eof: Cell::new(false),
out_spare: Cell::new(Vec::new()),
read_spare: Cell::new(Vec::new()),
}
}
async fn flush_tls(&self) -> io::Result<()> {
loop {
let mut out = self.out_spare.take();
out.clear();
{
let mut c = self.conn.borrow_mut();
if !c.wants_write() {
self.out_spare.set(out);
return Ok(());
}
while c.wants_write() {
c.write_tls(&mut out)?;
}
}
let (res, mut out) = self.io.clone().write_all_owned(out).await;
out.clear();
self.out_spare.set(out);
res?;
}
}
async fn fill_tls(&self) -> io::Result<()> {
let mut buf = self.read_spare.take();
buf.clear();
if buf.capacity() < READ_CHUNK {
buf.reserve(READ_CHUNK - buf.len());
}
let (res, mut buf) = self.io.clone().read_owned(buf).await;
let n = match res {
Ok(n) => n,
Err(e) => {
buf.clear();
self.read_spare.set(buf);
return Err(e);
}
};
let mut io_err: Option<io::Error> = None;
let outcome: Result<(), rustls::Error> = {
let mut c = self.conn.borrow_mut();
if n == 0 {
self.eof.set(true);
let _ = c.read_tls(&mut io::empty());
c.process_new_packets().map(|_| ())
} else {
let mut rest: &[u8] = &buf[..n];
let mut result = Ok(());
while !rest.is_empty() {
match c.read_tls(&mut rest) {
Ok(0) => break,
Ok(_) => {}
Err(e) => {
io_err = Some(e);
break;
}
}
if let Err(e) = c.process_new_packets() {
result = Err(e);
break;
}
}
result
}
};
buf.clear();
self.read_spare.set(buf);
if let Some(e) = io_err {
return Err(e);
}
match outcome {
Ok(()) => Ok(()),
Err(e) => {
let _ = self.flush_tls().await;
Err(tls_err(e))
}
}
}
async fn handshake(&self) -> io::Result<()> {
loop {
self.flush_tls().await?;
if !self.conn.borrow().is_handshaking() {
return Ok(());
}
if self.conn.borrow().wants_read() {
self.fill_tls().await?;
if self.eof.get() && self.conn.borrow().is_handshaking() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"connection closed during the TLS handshake",
));
}
}
}
}
pub async fn read<B>(&self, mut buf: B) -> BufResult<usize, B>
where
B: IoBufMut + Send + 'static,
{
let total = buf.bytes_total();
if total == 0 {
return (Ok(0), buf);
}
let want = total.min(READ_CHUNK);
let mut tmp = self.read_spare.take();
tmp.resize(want, 0);
loop {
let r = self.conn.borrow_mut().reader().read(&mut tmp);
match r {
Ok(n) => {
unsafe {
std::ptr::copy_nonoverlapping(tmp.as_ptr(), buf.stable_ptr_mut(), n);
buf.set_init(n);
}
self.read_spare.set(tmp);
return (Ok(n), buf);
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
if let Err(e) = self.fill_tls().await {
return (Err(e), buf);
}
if let Err(e) = self.flush_tls().await {
return (Err(e), buf);
}
}
Err(e) => return (Err(e), buf),
}
}
}
pub async fn read_exact<B>(&self, mut buf: B) -> BufResult<(), B>
where
B: IoBufMut + Send + 'static,
{
let total = buf.bytes_total();
let mut got = 0usize;
while got < total {
let tail = unsafe { buf.stable_ptr_mut().add(got) };
let (res, _) = self
.read(TailBuf {
ptr: tail,
len: total - got,
})
.await;
match res {
Ok(0) => {
let e =
io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer");
return (Err(e), buf);
}
Ok(n) => got += n,
Err(e) => return (Err(e), buf),
}
}
unsafe { buf.set_init(total) };
(Ok(()), buf)
}
pub async fn write_all<B>(&self, buf: B) -> BufResult<(), B>
where
B: IoBuf + Send + 'static,
{
let total = buf.bytes_init();
let mut off = 0usize;
while off < total {
let data =
unsafe { std::slice::from_raw_parts(buf.stable_ptr().add(off), total - off) };
let wrote = self.conn.borrow_mut().writer().write(data);
match wrote {
Ok(n) => off += n,
Err(e) => return (Err(e), buf),
}
if let Err(e) = self.flush_tls().await {
return (Err(e), buf);
}
}
(Ok(()), buf)
}
pub async fn write<B>(&self, buf: B) -> BufResult<usize, B>
where
B: IoBuf + Send + 'static,
{
let len = buf.bytes_init();
let (res, buf) = self.write_all(buf).await;
(res.map(|()| len), buf)
}
pub async fn flush(&self) -> io::Result<()> {
self.flush_tls().await
}
pub async fn shutdown(&self) -> io::Result<()> {
self.conn.borrow_mut().send_close_notify();
self.flush_tls().await?;
self.io.clone().shutdown_owned().await
}
pub fn alpn_protocol(&self) -> Option<Vec<u8>> {
self.conn.borrow().alpn_protocol().map(<[u8]>::to_vec)
}
pub fn get_ref(&self) -> &S {
&self.io
}
}
struct TailBuf {
ptr: *mut u8,
len: usize,
}
unsafe impl IoBufMut for TailBuf {
fn stable_ptr_mut(&mut self) -> *mut u8 {
self.ptr
}
fn bytes_total(&self) -> usize {
self.len
}
unsafe fn set_init(&mut self, _n: usize) {}
}
unsafe impl Send for TailBuf {}
impl Unpin for TailBuf {}
impl<S: Duplex> sealed::Sealed for TlsStream<S> {}
#[allow(clippy::manual_async_fn)]
impl<S: Duplex> Duplex for TlsStream<S> {
fn read_owned(
self: Rc<Self>,
buf: Vec<u8>,
) -> impl Future<Output = (io::Result<usize>, Vec<u8>)> + 'static {
async move { self.read(buf).await }
}
fn write_all_owned(
self: Rc<Self>,
buf: Vec<u8>,
) -> impl Future<Output = (io::Result<usize>, Vec<u8>)> + 'static {
async move { self.write(buf).await }
}
fn shutdown_owned(self: Rc<Self>) -> Pin<Box<dyn Future<Output = io::Result<()>>>> {
Box::pin(async move { self.shutdown().await })
}
}
#[derive(Clone)]
pub struct TlsConnector {
config: Arc<ClientConfig>,
}
impl TlsConnector {
pub fn new(config: Arc<ClientConfig>) -> Self {
Self { config }
}
pub async fn connect<S: Duplex>(
&self,
server_name: ServerName<'static>,
io: S,
) -> io::Result<TlsStream<S>> {
let conn = ClientConnection::new(self.config.clone(), server_name).map_err(tls_err)?;
let stream = TlsStream::new(io, Connection::Client(conn));
stream.handshake().await?;
Ok(stream)
}
}
#[derive(Clone)]
pub struct TlsAcceptor {
config: Arc<ServerConfig>,
}
impl TlsAcceptor {
pub fn new(config: Arc<ServerConfig>) -> Self {
Self { config }
}
pub async fn accept<S: Duplex>(&self, io: S) -> io::Result<TlsStream<S>> {
let conn = ServerConnection::new(self.config.clone()).map_err(tls_err)?;
let stream = TlsStream::new(io, Connection::Server(conn));
stream.handshake().await?;
Ok(stream)
}
}