//! Support for streams and listeners on `general::SocketAddr`. use async_trait::async_trait; use futures::{AsyncRead, AsyncWrite, StreamExt as _, stream}; use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult}; use std::net; use std::task::Poll; use std::{pin::Pin, task::Context}; use tor_general_addr::unix; use tracing::instrument; use crate::{NetStreamListener, NetStreamProvider, StreamOps}; use tor_general_addr::general; pub use general::{AddrParseError, SocketAddr}; /// Helper trait to allow us to create a type-erased stream. /// /// (Rust doesn't allow "dyn AsyncRead + AsyncWrite") trait ReadAndWrite: AsyncRead + AsyncWrite + StreamOps + Send + Sync {} impl ReadAndWrite for T where T: AsyncRead + AsyncWrite + StreamOps + Send + Sync {} /// A stream returned by a `NetStreamProvider` pub struct Stream(Pin>); impl AsyncRead for Stream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.0.as_mut().poll_read(cx, buf) } } impl AsyncWrite for Stream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.0.as_mut().poll_write(cx, buf) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.0.as_mut().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.0.as_mut().poll_close(cx) } } impl StreamOps for Stream { fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> { self.0.set_tcp_notsent_lowat(notsent_lowat) } fn new_handle(&self) -> Box { self.0.new_handle() } } /// The type of the result from an [`IncomingStreams`]. type StreamItem = IoResult<(Stream, general::SocketAddr)>; /// A stream of incoming connections on a [`general::Listener`](Listener). pub struct IncomingStreams(Pin + Send + Sync>>); impl stream::Stream for IncomingStreams { type Item = IoResult<(Stream, general::SocketAddr)>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.0.as_mut().poll_next(cx) } } /// A listener returned by a `NetStreamProvider`. pub struct Listener { /// The `futures::Stream` of incoming network streams. streams: IncomingStreams, /// The local address on which we're listening. local_addr: general::SocketAddr, } impl NetStreamListener for Listener { type Stream = Stream; type Incoming = IncomingStreams; fn incoming(self) -> IncomingStreams { self.streams } fn local_addr(&self) -> IoResult { Ok(self.local_addr.clone()) } } /// Use `provider` to launch a `NetStreamListener` at `address`, and wrap that listener /// as a `Listener`. async fn abstract_listener_on( provider: &P, address: &ADDR, options: &P::ListenOptions, ) -> IoResult where P: NetStreamProvider, general::SocketAddr: From, { let lis = provider.listen(address, options).await?; let local_addr = general::SocketAddr::from(lis.local_addr()?); let streams = lis.incoming().map(|result| { result.map(|(socket, addr)| (Stream(Box::pin(socket)), general::SocketAddr::from(addr))) }); let streams = IncomingStreams(Box::pin(streams)); Ok(Listener { streams, local_addr, }) } #[async_trait] impl NetStreamProvider for T where T: NetStreamProvider + NetStreamProvider, { type Stream = Stream; type Listener = Listener; // TODO: If unix sockets ever support `CommonConnectOptions`, // we could accept these common options and convert to the appropriate type. type ConnectOptions = (); // TODO: If unix sockets ever support `CommonListenOptions`, // we could accept these common options and convert to the appropriate type. type ListenOptions = (); #[instrument(skip_all, level = "trace")] async fn connect( &self, addr: &general::SocketAddr, (): &Self::ConnectOptions, ) -> IoResult { use general::SocketAddr as G; match addr { G::Inet(a) => { let options = Default::default(); Ok(Stream(Box::pin(self.connect(a, &options).await?))) } G::Unix(a) => { let options = Default::default(); Ok(Stream(Box::pin(self.connect(a, &options).await?))) } other => Err(IoError::new( IoErrorKind::InvalidInput, UnsupportedAddress(other.clone()), )), } } async fn listen( &self, addr: &general::SocketAddr, (): &Self::ListenOptions, ) -> IoResult { use general::SocketAddr as G; match addr { G::Inet(a) => abstract_listener_on(self, a, &Default::default()).await, G::Unix(a) => abstract_listener_on(self, a, &Default::default()).await, other => Err(IoError::new( IoErrorKind::InvalidInput, UnsupportedAddress(other.clone()), )), } } } /// Tried to use a [`general::SocketAddr`] that `tor-rtcompat` didn't understand. #[derive(Clone, Debug, thiserror::Error)] #[error("Socket address {0:?} is not supported by tor-rtcompat")] pub struct UnsupportedAddress(general::SocketAddr);