use std::os::fd::AsRawFd;
use std::pin::Pin;
use std::task::{Context, Poll};
use openssl::ssl::ShutdownResult;
use tokio::io::unix::AsyncFd;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use crate::error::Error;
use crate::ffi::BIO_NOCLOSE;
use foreign_types_shared::ForeignType;
#[derive(Debug)]
pub struct SslStream {
async_fd: AsyncFd<std::net::TcpStream>, ssl: openssl::ssl::Ssl,
}
enum ReactResult {
Final(Poll<Result<(), Error>>),
Retry,
}
fn react_to_ssl_call(
ssl_result: i32,
ssl: &openssl::ssl::Ssl,
async_fd: &AsyncFd<std::net::TcpStream>,
cx: &mut Context<'_>,
) -> ReactResult {
let ssl_error = unsafe { openssl_sys::SSL_get_error(ssl.as_ptr(), ssl_result) };
match ssl_error {
openssl_sys::SSL_ERROR_WANT_READ => {
match async_fd.poll_read_ready(cx) {
Poll::Ready(Ok(mut guard)) => {
guard.clear_ready();
ReactResult::Retry
}
Poll::Ready(Err(e)) => ReactResult::Final(Poll::Ready(Err(Error::from_io(e)))),
Poll::Pending => ReactResult::Final(Poll::Pending),
}
}
openssl_sys::SSL_ERROR_WANT_WRITE => {
match async_fd.poll_write_ready(cx) {
Poll::Ready(Ok(mut guard)) => {
guard.clear_ready();
ReactResult::Retry
}
Poll::Ready(Err(e)) => ReactResult::Final(Poll::Ready(Err(Error::from_io(e)))),
Poll::Pending => ReactResult::Final(Poll::Pending),
}
}
openssl_sys::SSL_ERROR_ZERO_RETURN => {
ReactResult::Final(Poll::Ready(Ok(())))
}
_ => {
ReactResult::Final(Poll::Ready(Err(Error::make(ssl_result, ssl))))
}
}
}
impl SslStream {
pub fn new(tcp_stream: tokio::net::TcpStream, ssl: openssl::ssl::Ssl) -> std::io::Result<Self> {
let std_tcp = tcp_stream.into_std().unwrap();
let sock_bio = unsafe { openssl_sys::BIO_new_socket(std_tcp.as_raw_fd(), BIO_NOCLOSE) };
assert!(!sock_bio.is_null(), "Failed to create socket BIO");
unsafe {
openssl_sys::SSL_set_bio(ssl.as_ptr(), sock_bio, sock_bio);
}
let async_fd = AsyncFd::new(std_tcp)?;
Ok(SslStream { ssl, async_fd })
}
pub fn get_ref(&self) -> &std::net::TcpStream {
self.async_fd.get_ref()
}
pub async fn connect(&self) -> Result<(), Error> {
use std::future::poll_fn;
poll_fn(|cx| self.poll_connect(cx)).await
}
pub fn poll_connect(&self, cx: &mut Context<'_>) -> Poll<Result<(), crate::error::Error>> {
loop {
let handshake_result = unsafe { openssl_sys::SSL_connect(self.ssl.as_ptr()) };
if handshake_result > 0 {
return Poll::Ready(Ok(()));
}
match react_to_ssl_call(handshake_result, &self.ssl, &self.async_fd, cx) {
ReactResult::Final(result) => return result,
ReactResult::Retry => continue, }
}
}
pub async fn accept(&mut self) -> Result<(), Error> {
use std::future::poll_fn;
poll_fn(|cx| self.poll_accept(cx)).await
}
pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
loop {
let accept_result = unsafe { openssl_sys::SSL_accept(self.ssl.as_ptr()) };
if accept_result > 0 {
return Poll::Ready(Ok(()));
}
match react_to_ssl_call(accept_result, &self.ssl, &self.async_fd, cx) {
ReactResult::Final(result) => return result,
ReactResult::Retry => continue, }
}
}
pub async fn ssl_shutdown(&mut self) -> Result<openssl::ssl::ShutdownResult, Error> {
use std::future::poll_fn;
poll_fn(|cx| self.poll_ssl_shutdown(cx)).await
}
pub fn poll_ssl_shutdown(
&mut self,
cx: &mut Context<'_>,
) -> Poll<Result<openssl::ssl::ShutdownResult, Error>> {
loop {
let result = unsafe { openssl_sys::SSL_shutdown(self.ssl.as_ptr()) };
match result {
1 => {
return Poll::Ready(Ok(openssl::ssl::ShutdownResult::Received));
}
0 => {
return Poll::Ready(Ok(ShutdownResult::Sent));
}
i => {
match react_to_ssl_call(i, &self.ssl, &self.async_fd, cx) {
ReactResult::Final(result) => {
return result
.map(|res| res.map(|_| openssl::ssl::ShutdownResult::Sent));
}
ReactResult::Retry => continue, }
}
}
}
}
pub fn ssl(&self) -> &openssl::ssl::Ssl {
&self.ssl
}
pub fn ktls_send_enabled(&self) -> bool {
unsafe {
let wbio = openssl_sys::SSL_get_wbio(self.ssl.as_ptr());
crate::ffi::BIO_get_ktls_send(wbio) != 0
}
}
pub fn ktls_recv_enabled(&self) -> bool {
unsafe {
let rbio = openssl_sys::SSL_get_rbio(self.ssl.as_ptr());
crate::ffi::BIO_get_ktls_recv(rbio) != 0
}
}
}
impl AsyncRead for SslStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let unfilled = unsafe { buf.unfilled_mut() };
if unfilled.is_empty() {
return Poll::Ready(Ok(()));
}
loop {
let mut readbytes = 0;
let ret = unsafe {
openssl_sys::SSL_read_ex(
self.ssl.as_ptr(),
unfilled.as_mut_ptr() as *mut _,
unfilled.len(),
&mut readbytes,
)
};
if ret > 0 {
unsafe { buf.assume_init(readbytes) }; buf.advance(readbytes); return Poll::Ready(Ok(()));
}
match react_to_ssl_call(ret, &self.ssl, &self.async_fd, cx) {
ReactResult::Final(result) => {
return result.map(|res| {
res.map_err(|arg0: Error| match arg0.into_io_error() {
Ok(io_e) => io_e,
Err(other) => std::io::Error::other(other),
})
});
}
ReactResult::Retry => continue, }
}
}
}
impl AsyncWrite for SslStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
if buf.is_empty() {
return Poll::Ready(Ok(0));
}
loop {
let mut written = 0;
let ret = unsafe {
openssl_sys::SSL_write_ex(
self.ssl.as_ptr(),
buf.as_ptr() as *const _,
buf.len(),
&mut written,
)
};
if ret > 0 {
return Poll::Ready(Ok(written));
} else {
match react_to_ssl_call(ret, &self.ssl, &self.async_fd, cx) {
ReactResult::Final(result) => {
return result.map(|res| {
res.map_err(|arg0: Error| match arg0.into_io_error() {
Ok(io_e) => io_e,
Err(other) => std::io::Error::other(other),
})
.map(|_| 0)
});
}
ReactResult::Retry => continue, }
}
}
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
match self.poll_ssl_shutdown(cx) {
Poll::Ready(Ok(_)) => Poll::Ready(Ok(())),
Poll::Ready(Err(e)) => Poll::Ready(Err(std::io::Error::other(e))),
Poll::Pending => Poll::Pending,
}
}
}
impl Drop for SslStream {
fn drop(&mut self) {
}
}