use std::io::{self, Read, Write};
use std::net::{Shutdown, SocketAddr, TcpStream};
use std::time::Duration;
pub trait NetStream: Read + Write + Send {
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
fn peer_addr(&self) -> io::Result<SocketAddr>;
fn local_addr(&self) -> io::Result<SocketAddr>;
fn shutdown(&self, how: Shutdown) -> io::Result<()>;
fn try_clone_box(&self) -> io::Result<Box<dyn NetStream>>;
}
impl NetStream for TcpStream {
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
TcpStream::set_read_timeout(self, dur)
}
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
TcpStream::set_write_timeout(self, dur)
}
fn peer_addr(&self) -> io::Result<SocketAddr> {
TcpStream::peer_addr(self)
}
fn local_addr(&self) -> io::Result<SocketAddr> {
TcpStream::local_addr(self)
}
fn shutdown(&self, how: Shutdown) -> io::Result<()> {
TcpStream::shutdown(self, how)
}
fn try_clone_box(&self) -> io::Result<Box<dyn NetStream>> {
Ok(Box::new(TcpStream::try_clone(self)?))
}
}
#[cfg(unix)]
impl NetStream for std::os::unix::net::UnixStream {
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
std::os::unix::net::UnixStream::set_read_timeout(self, dur)
}
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
std::os::unix::net::UnixStream::set_write_timeout(self, dur)
}
fn peer_addr(&self) -> io::Result<SocketAddr> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"peer_addr unavailable on a Unix-domain socket",
))
}
fn local_addr(&self) -> io::Result<SocketAddr> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"local_addr unavailable on a Unix-domain socket",
))
}
fn shutdown(&self, how: Shutdown) -> io::Result<()> {
std::os::unix::net::UnixStream::shutdown(self, how)
}
fn try_clone_box(&self) -> io::Result<Box<dyn NetStream>> {
Ok(Box::new(std::os::unix::net::UnixStream::try_clone(self)?))
}
}
pub(crate) enum MaybeTlsStream {
Plain(Box<dyn NetStream>),
Tls(Box<crate::tls::TlsStream<Box<dyn NetStream>>>),
Upgrading,
}
impl MaybeTlsStream {
pub(crate) fn is_plain(&self) -> bool {
matches!(self, Self::Plain(_))
}
pub(crate) fn upgrade(&mut self, host: &str) -> crate::error::Result<()> {
let plain = match std::mem::replace(self, Self::Upgrading) {
Self::Plain(s) => s,
other => {
*self = other;
return Err(crate::error::Error::BadResponse(
"STARTTLS requested on a non-plaintext connection".into(),
));
}
};
let tls = crate::tls::connect_over(plain, host)?;
*self = Self::Tls(Box::new(tls));
Ok(())
}
}
impl Read for MaybeTlsStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Self::Plain(s) => s.read(buf),
Self::Tls(s) => s.read(buf),
Self::Upgrading => Err(io::Error::other("tls upgrade in progress")),
}
}
}
impl Write for MaybeTlsStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
Self::Plain(s) => s.write(buf),
Self::Tls(s) => s.write(buf),
Self::Upgrading => Err(io::Error::other("tls upgrade in progress")),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
Self::Plain(s) => s.flush(),
Self::Tls(s) => s.flush(),
Self::Upgrading => Err(io::Error::other("tls upgrade in progress")),
}
}
}