use futures_util::future::Either;
use futures_util::{pin_mut, ready, TryFuture};
use pin_project_lite::pin_project;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::{error, fmt, io};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::TcpStream;
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
use tokio_rustls::rustls::KeyLogFile;
use tokio_rustls::server::TlsStream;
use tokio_rustls::{Accept, TlsAcceptor};
pub use tokio_rustls::rustls::ServerConfig;
const SSLKEYLOGFILE_ENV_VAR_NAME: &str = "SSLKEYLOGFILE";
pub fn create_server_config(
key_path: &Path,
cert_path: &Path,
) -> Result<ServerConfig, TlsConfigError> {
let mut config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(read_certs(cert_path)?, read_key(key_path)?)
.map_err(|err| TlsConfigError::other(ErrorKind::Tls, err))?;
if std::env::var(SSLKEYLOGFILE_ENV_VAR_NAME).is_ok() {
config.key_log = Arc::new(KeyLogFile::new());
}
Ok(config)
}
fn read_certs(
path: &Path,
) -> Result<Vec<CertificateDer<'static>>, TlsConfigError> {
rustls_pemfile::certs(&mut io::BufReader::new(File::open(path).map_err(
|err| TlsConfigError::new(ErrorKind::Cert(path.into()), err),
)?))
.collect::<Result<_, _>>()
.map_err(|err| TlsConfigError::new(ErrorKind::Cert(path.into()), err))
}
fn read_key(path: &Path) -> Result<PrivateKeyDer<'static>, TlsConfigError> {
use rustls_pemfile::Item::*;
let mut key_file =
io::BufReader::new(File::open(path).map_err(|err| {
TlsConfigError::new(ErrorKind::Key(path.into()), err)
})?);
let mut key = None;
while let Some(item) = rustls_pemfile::read_one(&mut key_file).transpose()
{
let item = item.map_err(|err| {
TlsConfigError::new(ErrorKind::Key(path.into()), err)
})?;
let bits = match item {
Pkcs1Key(bits) => bits.into(),
Pkcs8Key(bits) => bits.into(),
Sec1Key(bits) => bits.into(),
_ => continue,
};
if key.is_some() {
return Err(TlsConfigError::other(
ErrorKind::Key(path.into()),
"file contains multiple keys",
));
}
key = Some(bits)
}
key.ok_or_else(|| {
TlsConfigError::other(
ErrorKind::Key(path.into()),
"file does not contain any usable keys",
)
})
}
pin_project! {
#[project = TlsTcpStreamProj]
enum TlsTcpStream {
Accept { #[pin] fut: Accept<TcpStream> },
Stream { #[pin] fut: TlsStream<TcpStream> },
Empty,
}
}
impl TlsTcpStream {
fn new(sock: TcpStream, tls: &TlsAcceptor) -> Self {
Self::Accept {
fut: tls.accept(sock),
}
}
fn poll_accept(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Pin<&mut Self>, io::Error>> {
match self.as_mut().project() {
TlsTcpStreamProj::Accept { fut } => {
match ready!(fut.try_poll(cx)) {
Ok(fut) => {
self.set(Self::Stream { fut });
Poll::Ready(Ok(self))
}
Err(err) => {
self.set(Self::Empty);
Poll::Ready(Err(err))
}
}
}
_ => Poll::Ready(Ok(self)),
}
}
}
impl AsyncRead for TlsTcpStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<Result<(), io::Error>> {
let mut this = match ready!(self.poll_accept(cx)) {
Ok(this) => this,
Err(err) => return Poll::Ready(Err(err)),
};
match this.as_mut().project() {
TlsTcpStreamProj::Stream { fut } => fut.poll_read(cx, buf),
TlsTcpStreamProj::Empty => Poll::Ready(Ok(())),
_ => unreachable!(),
}
}
}
impl AsyncWrite for TlsTcpStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
let mut this = match ready!(self.poll_accept(cx)) {
Ok(this) => this,
Err(err) => return Poll::Ready(Err(err)),
};
match this.as_mut().project() {
TlsTcpStreamProj::Stream { fut } => fut.poll_write(cx, buf),
TlsTcpStreamProj::Empty => Poll::Ready(Ok(0)),
_ => unreachable!(),
}
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), io::Error>> {
let mut this = match ready!(self.poll_accept(cx)) {
Ok(this) => this,
Err(err) => return Poll::Ready(Err(err)),
};
match this.as_mut().project() {
TlsTcpStreamProj::Stream { fut } => fut.poll_flush(cx),
TlsTcpStreamProj::Empty => Poll::Ready(Ok(())),
_ => unreachable!(),
}
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), io::Error>> {
let mut this = match ready!(self.poll_accept(cx)) {
Ok(this) => this,
Err(err) => return Poll::Ready(Err(err)),
};
match this.as_mut().project() {
TlsTcpStreamProj::Stream { fut } => fut.poll_shutdown(cx),
TlsTcpStreamProj::Empty => Poll::Ready(Ok(())),
_ => unreachable!(),
}
}
}
pub struct MaybeTlsTcpStream {
sock: Either<TcpStream, TlsTcpStream>,
}
impl MaybeTlsTcpStream {
pub fn new(sock: TcpStream, tls: Option<&TlsAcceptor>) -> Self {
MaybeTlsTcpStream {
sock: match tls {
Some(tls) => Either::Right(TlsTcpStream::new(sock, tls)),
None => Either::Left(sock),
},
}
}
}
impl AsyncRead for MaybeTlsTcpStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut ReadBuf,
) -> Poll<Result<(), io::Error>> {
match self.sock {
Either::Left(ref mut sock) => {
pin_mut!(sock);
sock.poll_read(cx, buf)
}
Either::Right(ref mut sock) => {
pin_mut!(sock);
sock.poll_read(cx, buf)
}
}
}
}
impl AsyncWrite for MaybeTlsTcpStream {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
match self.sock {
Either::Left(ref mut sock) => {
pin_mut!(sock);
sock.poll_write(cx, buf)
}
Either::Right(ref mut sock) => {
pin_mut!(sock);
sock.poll_write(cx, buf)
}
}
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Result<(), io::Error>> {
match self.sock {
Either::Left(ref mut sock) => {
pin_mut!(sock);
sock.poll_flush(cx)
}
Either::Right(ref mut sock) => {
pin_mut!(sock);
sock.poll_flush(cx)
}
}
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Result<(), io::Error>> {
match self.sock {
Either::Left(ref mut sock) => {
pin_mut!(sock);
sock.poll_shutdown(cx)
}
Either::Right(ref mut sock) => {
pin_mut!(sock);
sock.poll_shutdown(cx)
}
}
}
}
#[derive(Debug)]
pub struct TlsConfigError {
kind: ErrorKind,
err: io::Error,
}
#[derive(Clone, Debug)]
enum ErrorKind {
Key(PathBuf),
Cert(PathBuf),
Tls,
}
impl TlsConfigError {
fn new(kind: ErrorKind, err: io::Error) -> Self {
Self { kind, err }
}
fn other(
kind: ErrorKind,
err: impl Into<Box<dyn error::Error + Send + Sync>>,
) -> Self {
Self {
kind,
err: io::Error::other(err),
}
}
}
impl fmt::Display for TlsConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ErrorKind::Key(ref path) => {
write!(
f,
"Error in TLS key file {}: {}",
path.display(),
self.err
)
}
ErrorKind::Cert(ref path) => {
write!(
f,
"Error in TLS certificate file {}: {}",
path.display(),
self.err
)
}
ErrorKind::Tls => {
write!(f, "Error in TLS configuration: {}", self.err)
}
}
}
}
impl error::Error for TlsConfigError {}