mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-13 01:51:23 +00:00
239 lines
8.0 KiB
Rust
239 lines
8.0 KiB
Rust
// Copyright 2018 Parity Technologies (UK) Ltd.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
//! Implementation of the libp2p `Transport` trait for Unix domain sockets.
|
|
//!
|
|
//! Uses [the *tokio* library](https://tokio.rs).
|
|
//!
|
|
//! # Platform support
|
|
//!
|
|
//! This transport only works on Unix platforms.
|
|
//!
|
|
//! # Usage
|
|
//!
|
|
//! The `UdsConfig` transport supports multiaddresses of the form `/unix//tmp/foo`.
|
|
//!
|
|
//! Example:
|
|
//!
|
|
//! ```
|
|
//! extern crate libp2p_uds;
|
|
//! use libp2p_uds::UdsConfig;
|
|
//!
|
|
//! # fn main() {
|
|
//! let uds = UdsConfig::new();
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! The `UdsConfig` structs implements the `Transport` trait of the `core` library. See the
|
|
//! documentation of `core` and of libp2p in general to learn how to use the `Transport` trait.
|
|
|
|
#![cfg(all(unix, not(target_os = "emscripten")))]
|
|
|
|
extern crate futures;
|
|
extern crate libp2p_core;
|
|
#[macro_use]
|
|
extern crate log;
|
|
extern crate multiaddr;
|
|
extern crate tokio_uds;
|
|
|
|
#[cfg(test)]
|
|
extern crate tempfile;
|
|
#[cfg(test)]
|
|
extern crate tokio_current_thread;
|
|
#[cfg(test)]
|
|
extern crate tokio_io;
|
|
|
|
use futures::future::{self, Future, FutureResult};
|
|
use futures::stream::Stream;
|
|
use multiaddr::{AddrComponent, Multiaddr};
|
|
use std::io::Error as IoError;
|
|
use std::path::PathBuf;
|
|
use libp2p_core::Transport;
|
|
use tokio_uds::{UnixListener, UnixStream};
|
|
|
|
/// Represents the configuration for a Unix domain sockets transport capability for libp2p.
|
|
///
|
|
/// The Unixs sockets created by libp2p will need to be progressed by running the futures and
|
|
/// streams obtained by libp2p through the tokio reactor.
|
|
#[derive(Debug, Clone)]
|
|
pub struct UdsConfig {
|
|
}
|
|
|
|
impl UdsConfig {
|
|
/// Creates a new configuration object for TCP/IP.
|
|
#[inline]
|
|
pub fn new() -> UdsConfig {
|
|
UdsConfig {}
|
|
}
|
|
}
|
|
|
|
impl Transport for UdsConfig {
|
|
type Output = UnixStream;
|
|
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>;
|
|
type ListenerUpgrade = FutureResult<(Self::Output, Self::MultiaddrFuture), IoError>;
|
|
type MultiaddrFuture = FutureResult<Multiaddr, IoError>;
|
|
type Dial = Box<Future<Item = (UnixStream, Self::MultiaddrFuture), Error = IoError>>;
|
|
|
|
fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
|
|
if let Ok(path) = multiaddr_to_path(&addr) {
|
|
let listener = UnixListener::bind(&path);
|
|
// We need to build the `Multiaddr` to return from this function. If an error happened,
|
|
// just return the original multiaddr.
|
|
match listener {
|
|
Ok(_) => {},
|
|
Err(_) => return Err((self, addr)),
|
|
};
|
|
|
|
debug!("Now listening on {}", addr);
|
|
let new_addr = addr.clone();
|
|
|
|
let future = future::result(listener)
|
|
.map(move |listener| {
|
|
// Pull out a stream of sockets for incoming connections
|
|
listener.incoming().map(move |sock| {
|
|
debug!("Incoming connection on {}", addr);
|
|
future::ok((sock, future::ok(addr.clone())))
|
|
})
|
|
})
|
|
.flatten_stream();
|
|
Ok((Box::new(future), new_addr))
|
|
} else {
|
|
Err((self, addr))
|
|
}
|
|
}
|
|
|
|
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
|
|
if let Ok(path) = multiaddr_to_path(&addr) {
|
|
debug!("Dialing {}", addr);
|
|
let fut = UnixStream::connect(&path).map(|t| (t, future::ok(addr)));
|
|
Ok(Box::new(fut) as Box<_>)
|
|
} else {
|
|
Err((self, addr))
|
|
}
|
|
}
|
|
|
|
fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
|
|
if server == observed {
|
|
Some(observed.clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
// This type of logic should probably be moved into the multiaddr package
|
|
fn multiaddr_to_path(addr: &Multiaddr) -> Result<PathBuf, ()> {
|
|
let mut iter = addr.iter();
|
|
let path = iter.next();
|
|
|
|
if iter.next().is_some() {
|
|
return Err(());
|
|
}
|
|
|
|
match path {
|
|
Some(AddrComponent::UNIX(ref path)) => Ok(path.into()),
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{multiaddr_to_path, UdsConfig};
|
|
use futures::stream::Stream;
|
|
use futures::Future;
|
|
use multiaddr::{AddrComponent, Multiaddr};
|
|
use std;
|
|
use std::path::Path;
|
|
use libp2p_core::Transport;
|
|
use tempfile;
|
|
use tokio_current_thread;
|
|
use tokio_io;
|
|
|
|
#[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(AddrComponent::UNIX("/tmp/foo".into()))),
|
|
Ok(Path::new("/tmp/foo").to_owned())
|
|
);
|
|
assert_eq!(
|
|
multiaddr_to_path(&Multiaddr::from(AddrComponent::UNIX("/home/bar/baz".into()))),
|
|
Ok(Path::new("/home/bar/baz").to_owned())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn communicating_between_dialer_and_listener() {
|
|
use std::io::Write;
|
|
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
let socket = temp_dir.path().join("socket");
|
|
let addr = Multiaddr::from(AddrComponent::UNIX(socket.to_string_lossy().into_owned()));
|
|
let addr2 = addr.clone();
|
|
|
|
std::thread::spawn(move || {
|
|
let tcp = UdsConfig::new();
|
|
let listener = tcp.listen_on(addr2).unwrap().0.for_each(|sock| {
|
|
sock.and_then(|(sock, _)| {
|
|
// Define what to do with the socket that just connected to us
|
|
// Which in this case is read 3 bytes
|
|
let handle_conn = tokio_io::io::read_exact(sock, [0; 3])
|
|
.map(|(_, buf)| assert_eq!(buf, [1, 2, 3]))
|
|
.map_err(|err| panic!("IO error {:?}", err));
|
|
|
|
// Spawn the future as a concurrent task
|
|
tokio_current_thread::spawn(handle_conn);
|
|
|
|
Ok(())
|
|
})
|
|
});
|
|
|
|
tokio_current_thread::block_on_all(listener).unwrap();
|
|
});
|
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
let tcp = UdsConfig::new();
|
|
// Obtain a future socket through dialing
|
|
let socket = tcp.dial(addr.clone()).unwrap();
|
|
// Define what to do with the socket once it's obtained
|
|
let action = socket.then(|sock| -> Result<(), ()> {
|
|
sock.unwrap().0.write(&[0x1, 0x2, 0x3]).unwrap();
|
|
Ok(())
|
|
});
|
|
// Execute the future in our event loop
|
|
tokio_current_thread::block_on_all(action).unwrap();
|
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
}
|
|
|
|
#[test]
|
|
#[ignore] // TODO: for the moment unix addresses fail to parse
|
|
fn larger_addr_denied() {
|
|
let tcp = UdsConfig::new();
|
|
|
|
let addr = "/ip4/127.0.0.1/tcp/12345/unix//foo/bar"
|
|
.parse::<Multiaddr>()
|
|
.unwrap();
|
|
assert!(tcp.listen_on(addr).is_err());
|
|
}
|
|
}
|