#![cfg(all(
unix,
not(target_os = "emscripten"),
any(feature = "tokio", feature = "async-std")
))]
#![cfg_attr(docsrs, doc(cfg(all(unix, not(target_os = "emscripten")))))]
use futures::stream::BoxStream;
use futures::{
future::{BoxFuture, Ready},
prelude::*,
};
use libp2p_core::transport::ListenerId;
use libp2p_core::{
multiaddr::{Multiaddr, Protocol},
transport::{TransportError, TransportEvent},
Transport,
};
use log::debug;
use std::collections::VecDeque;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{io, path::PathBuf};
pub type Listener<T> = BoxStream<
'static,
Result<
TransportEvent<<T as Transport>::ListenerUpgrade, <T as Transport>::Error>,
Result<(), <T as Transport>::Error>,
>,
>;
macro_rules! codegen {
($feature_name:expr, $uds_config:ident, $build_listener:expr, $unix_stream:ty, $($mut_or_not:tt)*) => {
#[cfg_attr(docsrs, doc(cfg(feature = $feature_name)))]
pub struct $uds_config {
listeners: VecDeque<(ListenerId, Listener<Self>)>,
}
impl $uds_config {
pub fn new() -> $uds_config {
$uds_config {
listeners: VecDeque::new(),
}
}
}
impl Default for $uds_config {
fn default() -> Self {
Self::new()
}
}
impl Transport for $uds_config {
type Output = $unix_stream;
type Error = io::Error;
type ListenerUpgrade = Ready<Result<Self::Output, Self::Error>>;
type Dial = BoxFuture<'static, Result<Self::Output, Self::Error>>;
fn listen_on(
&mut self,
addr: Multiaddr,
) -> Result<ListenerId, TransportError<Self::Error>> {
if let Ok(path) = multiaddr_to_path(&addr) {
let id = ListenerId::new();
let listener = $build_listener(path)
.map_err(Err)
.map_ok(move |listener| {
stream::once({
let addr = addr.clone();
async move {
debug!("Now listening on {}", addr);
Ok(TransportEvent::NewAddress {
listener_id: id,
listen_addr: addr,
})
}
})
.chain(stream::unfold(
listener,
move |listener| {
let addr = addr.clone();
async move {
let event = match listener.accept().await {
Ok((stream, _)) => {
debug!("incoming connection on {}", addr);
TransportEvent::Incoming {
upgrade: future::ok(stream),
local_addr: addr.clone(),
send_back_addr: addr.clone(),
listener_id: id,
}
}
Err(error) => TransportEvent::ListenerError {
listener_id: id,
error,
},
};
Some((Ok(event), listener))
}
},
))
})
.try_flatten_stream()
.boxed();
self.listeners.push_back((id, listener));
Ok(id)
} else {
Err(TransportError::MultiaddrNotSupported(addr))
}
}
fn remove_listener(&mut self, id: ListenerId) -> bool {
if let Some(index) = self
.listeners
.iter()
.position(|(listener_id, _)| listener_id == &id)
{
let listener_stream = self.listeners.get_mut(index).unwrap();
let report_closed_stream = stream::once(async { Err(Ok(())) }).boxed();
*listener_stream = (id, report_closed_stream);
true
} else {
false
}
}
fn dial(&mut self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
if let Ok(path) = multiaddr_to_path(&addr) {
debug!("Dialing {}", addr);
Ok(async move { <$unix_stream>::connect(&path).await }.boxed())
} else {
Err(TransportError::MultiaddrNotSupported(addr))
}
}
fn dial_as_listener(
&mut self,
addr: Multiaddr,
) -> Result<Self::Dial, TransportError<Self::Error>> {
self.dial(addr)
}
fn address_translation(
&self,
_server: &Multiaddr,
_observed: &Multiaddr,
) -> Option<Multiaddr> {
None
}
fn poll(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
let mut remaining = self.listeners.len();
while let Some((id, mut listener)) = self.listeners.pop_back() {
let event = match Stream::poll_next(Pin::new(&mut listener), cx) {
Poll::Pending => None,
Poll::Ready(None) => panic!("Alive listeners always have a sender."),
Poll::Ready(Some(Ok(event))) => Some(event),
Poll::Ready(Some(Err(reason))) => {
return Poll::Ready(TransportEvent::ListenerClosed {
listener_id: id,
reason,
})
}
};
self.listeners.push_front((id, listener));
if let Some(event) = event {
return Poll::Ready(event);
} else {
remaining -= 1;
if remaining == 0 {
break;
}
}
}
Poll::Pending
}
}
};
}
#[cfg(feature = "async-std")]
codegen!(
"async-std",
UdsConfig,
|addr| async move { async_std::os::unix::net::UnixListener::bind(&addr).await },
async_std::os::unix::net::UnixStream,
);
#[cfg(feature = "tokio")]
codegen!(
"tokio",
TokioUdsConfig,
|addr| async move { tokio::net::UnixListener::bind(&addr) },
tokio::net::UnixStream,
);
fn multiaddr_to_path(addr: &Multiaddr) -> Result<PathBuf, ()> {
let mut protocols = addr.iter();
match protocols.next() {
Some(Protocol::Unix(ref path)) => {
let path = PathBuf::from(path.as_ref());
if !path.is_absolute() {
return Err(());
}
match protocols.next() {
None | Some(Protocol::P2p(_)) => Ok(path),
Some(_) => Err(()),
}
}
_ => Err(()),
}
}
#[cfg(all(test, feature = "async-std"))]
mod tests {
use super::{multiaddr_to_path, UdsConfig};
use futures::{channel::oneshot, prelude::*};
use libp2p_core::{
multiaddr::{Multiaddr, Protocol},
Transport,
};
use std::{self, borrow::Cow, path::Path};
use tempfile;
#[test]
fn multiaddr_to_path_conversion() {
assert!(
multiaddr_to_path(&"/ip4/127.0.0.1/udp/1234".parse::<Multiaddr>().unwrap()).is_err()
);
assert_eq!(
multiaddr_to_path(&Multiaddr::from(Protocol::Unix("/tmp/foo".into()))),
Ok(Path::new("/tmp/foo").to_owned())
);
assert_eq!(
multiaddr_to_path(&Multiaddr::from(Protocol::Unix("/home/bar/baz".into()))),
Ok(Path::new("/home/bar/baz").to_owned())
);
}
#[test]
fn communicating_between_dialer_and_listener() {
let temp_dir = tempfile::tempdir().unwrap();
let socket = temp_dir.path().join("socket");
let addr = Multiaddr::from(Protocol::Unix(Cow::Owned(
socket.to_string_lossy().into_owned(),
)));
let (tx, rx) = oneshot::channel();
async_std::task::spawn(async move {
let mut transport = UdsConfig::new().boxed();
transport.listen_on(addr).unwrap();
let listen_addr = transport
.select_next_some()
.await
.into_new_address()
.expect("listen address");
tx.send(listen_addr).unwrap();
let (sock, _addr) = transport
.select_next_some()
.await
.into_incoming()
.expect("incoming stream");
let mut sock = sock.await.unwrap();
let mut buf = [0u8; 3];
sock.read_exact(&mut buf).await.unwrap();
assert_eq!(buf, [1, 2, 3]);
});
async_std::task::block_on(async move {
let mut uds = UdsConfig::new();
let addr = rx.await.unwrap();
let mut socket = uds.dial(addr).unwrap().await.unwrap();
socket.write(&[1, 2, 3]).await.unwrap();
});
}
#[test]
#[ignore] fn larger_addr_denied() {
let mut uds = UdsConfig::new();
let addr = "/unix//foo/bar".parse::<Multiaddr>().unwrap();
assert!(uds.listen_on(addr).is_err());
}
#[test]
#[ignore] fn relative_addr_denied() {
assert!("/unix/./foo/bar".parse::<Multiaddr>().is_err());
}
}