rust-libp2p/protocols/identify/src/identify_transport.rs

334 lines
14 KiB
Rust
Raw Normal View History

2018-07-11 14:35:24 +02:00
// 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.
use fnv::FnvHashMap;
use futures::{future, Future, Stream};
use libp2p_core::{Multiaddr, MuxedTransport, Transport};
use parking_lot::Mutex;
use protocol::{IdentifyInfo, IdentifyOutput, IdentifyProtocolConfig};
use std::collections::hash_map::Entry;
2018-07-17 23:26:50 +02:00
use std::error::Error;
2018-07-11 14:35:24 +02:00
use std::io::Error as IoError;
use std::sync::Arc;
use tokio_io::{AsyncRead, AsyncWrite};
/// Implementation of `Transport`. See [the crate root description](index.html).
pub struct IdentifyTransport<Trans> {
transport: Trans,
// Each entry is protected by an asynchronous mutex, so that if we dial the same node twice
// simultaneously, the second time will block until the first time has identified it.
cache: Arc<Mutex<FnvHashMap<Multiaddr, CacheEntry>>>,
}
impl<Trans> Clone for IdentifyTransport<Trans>
where Trans: Clone,
{
fn clone(&self) -> Self {
IdentifyTransport {
transport: self.transport.clone(),
cache: self.cache.clone(),
}
}
}
2018-09-06 09:54:35 +02:00
type CacheEntry = future::Shared<Box<Future<Item = IdentifyTransportOutcome, Error = IoError> + Send>>;
2018-07-11 14:35:24 +02:00
impl<Trans> IdentifyTransport<Trans> {
/// Creates an `IdentifyTransport` that wraps around the given transport and peerstore.
#[inline]
pub fn new(transport: Trans) -> Self {
IdentifyTransport {
transport,
cache: Arc::new(Mutex::new(Default::default())),
}
}
}
impl<Trans> Transport for IdentifyTransport<Trans>
where
2018-09-06 09:54:35 +02:00
Trans: Transport + Clone + Send + 'static, // TODO: 'static :(
Trans::Dial: Send,
Trans::Listener: Send,
Trans::ListenerUpgrade: Send,
Trans::MultiaddrFuture: Send,
Trans::Output: AsyncRead + AsyncWrite + Send,
2018-07-11 14:35:24 +02:00
{
type Output = IdentifyTransportOutput<Trans::Output>;
type MultiaddrFuture = future::FutureResult<Multiaddr, IoError>;
2018-09-06 09:54:35 +02:00
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError> + Send>;
type ListenerUpgrade = Box<Future<Item = (Self::Output, Self::MultiaddrFuture), Error = IoError> + Send>;
type Dial = Box<Future<Item = (Self::Output, Self::MultiaddrFuture), Error = IoError> + Send>;
2018-07-11 14:35:24 +02:00
#[inline]
fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
let (listener, new_addr) = match self.transport.clone().listen_on(addr.clone()) {
Ok((l, a)) => (l, a),
Err((inner, addr)) => {
let id = IdentifyTransport {
transport: inner,
cache: self.cache,
};
return Err((id, addr));
}
};
let identify_upgrade = self.transport.with_upgrade(IdentifyProtocolConfig);
let cache = self.cache.clone();
let listener = listener.map(move |connec| {
let identify_upgrade = identify_upgrade.clone();
let cache = cache.clone();
let fut = connec
.and_then(move |(connec, client_addr)| {
trace!("Incoming connection, waiting for client address");
client_addr.map(move |addr| (connec, addr))
})
.and_then(move |(connec, client_addr)| {
debug!("Incoming connection from {}", client_addr);
// Dial the address that connected to us and try upgrade with the
// identify protocol.
let info_future = cache_entry(&cache, client_addr.clone(), { let client_addr = client_addr.clone(); move || {
debug!("No cache entry for {}, dialing back in order to identify", client_addr);
future::lazy(|| { trace!("Starting identify back"); identify_upgrade
.dial(client_addr)
.unwrap_or_else(|(_, addr)| {
panic!("the multiaddr {} was determined to be valid earlier", addr)
}) })
.map(move |(identify, _)| {
let (info, observed_addr) = match identify {
IdentifyOutput::RemoteInfo { info, observed_addr } => {
(info, observed_addr)
},
_ => unreachable!(
"the identify protocol guarantees that we receive \
remote information when we dial a node"
),
};
debug!("Identified dialed back connection as pubkey {:?}", info.public_key);
IdentifyTransportOutcome {
info,
observed_addr,
}
})
.map_err(move |err| {
debug!("Failed to identify dialed back connection");
err
})
}});
let out = IdentifyTransportOutput {
socket: connec,
info: Box::new(info_future),
};
Ok((out, future::ok(client_addr)))
});
2018-09-06 09:54:35 +02:00
Box::new(fut) as Box<Future<Item = _, Error = _> + Send>
2018-07-11 14:35:24 +02:00
});
Ok((Box::new(listener) as Box<_>, new_addr))
}
#[inline]
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
// We dial a first time the node.
let dial = match self.transport.clone().dial(addr) {
Ok(d) => d,
Err((transport, addr)) => {
let id = IdentifyTransport {
transport,
cache: self.cache,
};
return Err((id, addr));
}
};
// Once successfully dialed, we dial again to identify.
let identify_upgrade = self.transport.with_upgrade(IdentifyProtocolConfig);
let cache = self.cache.clone();
let future = dial
.and_then(move |(connec, client_addr)| {
trace!("Dialing successful, waiting for client address");
client_addr.map(move |addr| (connec, addr))
})
.and_then(move |(socket, addr)| {
trace!("Dialing successful ; client address is {}", addr);
let info_future = cache_entry(&cache, addr.clone(), { let addr = addr.clone(); move || {
trace!("No cache entry for {} ; dialing again for identification", addr);
future::lazy(|| { trace!("Starting identify back"); identify_upgrade
.dial(addr)
.unwrap_or_else(|(_, addr)| {
panic!("the multiaddr {} was determined to be valid earlier", addr)
}) })
.map(move |(identify, _)| {
let (info, observed_addr) = match identify {
IdentifyOutput::RemoteInfo { info, observed_addr } => {
(info, observed_addr)
}
_ => unreachable!(
"the identify protocol guarantees that we receive \
remote information when we dial a node"
),
};
IdentifyTransportOutcome {
info,
observed_addr,
}
})
}});
let out = IdentifyTransportOutput {
socket: socket,
info: Box::new(info_future),
};
Ok((out, future::ok(addr)))
});
Ok(Box::new(future) as Box<_>)
}
#[inline]
fn nat_traversal(&self, a: &Multiaddr, b: &Multiaddr) -> Option<Multiaddr> {
self.transport.nat_traversal(a, b)
}
}
impl<Trans> MuxedTransport for IdentifyTransport<Trans>
where
2018-09-06 09:54:35 +02:00
Trans: MuxedTransport + Clone + Send + 'static,
Trans::Dial: Send,
Trans::Listener: Send,
Trans::ListenerUpgrade: Send,
Trans::MultiaddrFuture: Send,
Trans::Output: AsyncRead + AsyncWrite + Send,
Trans::Incoming: Send,
Trans::IncomingUpgrade: Send,
2018-07-11 14:35:24 +02:00
{
2018-09-06 09:54:35 +02:00
type Incoming = Box<Future<Item = Self::IncomingUpgrade, Error = IoError> + Send>;
type IncomingUpgrade = Box<Future<Item = (Self::Output, Self::MultiaddrFuture), Error = IoError> + Send>;
2018-07-11 14:35:24 +02:00
#[inline]
fn next_incoming(self) -> Self::Incoming {
let identify_upgrade = self.transport.clone().with_upgrade(IdentifyProtocolConfig);
let cache = self.cache.clone();
let future = self.transport.next_incoming().map(move |incoming| {
let cache = cache.clone();
let future = incoming
.and_then(move |(connec, client_addr)| {
debug!("Incoming substream ; waiting for client address");
client_addr.map(move |addr| (connec, addr))
})
.and_then(move |(connec, client_addr)| {
debug!("Incoming substream from {}", client_addr);
// Dial the address that connected to us and try upgrade with the
// identify protocol.
let info_future = cache_entry(&cache, client_addr.clone(), { let client_addr = client_addr.clone(); move || {
debug!("No cache entry from {} ; dialing back to identify", client_addr);
future::lazy(|| { trace!("Starting identify back"); identify_upgrade
.dial(client_addr)
.unwrap_or_else(|(_, client_addr)| {
panic!("the multiaddr {} was determined to be valid earlier", client_addr)
}) })
.map(move |(identify, _)| {
let (info, observed_addr) = match identify {
IdentifyOutput::RemoteInfo { info, observed_addr } => {
(info, observed_addr)
},
_ => unreachable!(
"the identify protocol guarantees that we receive \
remote information when we dial a node"
),
};
debug!("Identified incoming substream as pubkey {:?}", info.public_key);
IdentifyTransportOutcome {
info,
observed_addr,
}
})
.map_err(move |err| {
debug!("Failed to identify incoming substream");
err
})
}});
let out = IdentifyTransportOutput {
socket: connec,
info: Box::new(info_future),
};
Ok((out, future::ok(client_addr)))
});
2018-09-06 09:54:35 +02:00
Box::new(future) as Box<Future<Item = _, Error = _> + Send>
2018-07-11 14:35:24 +02:00
});
Box::new(future) as Box<_>
}
}
/// Output of the identify transport.
pub struct IdentifyTransportOutput<S> {
/// The socket to communicate with the remote.
pub socket: S,
/// Outcome of the identification of the remote.
2018-09-06 09:54:35 +02:00
pub info: Box<Future<Item = IdentifyTransportOutcome, Error = IoError> + Send>,
2018-07-11 14:35:24 +02:00
}
/// Outcome of the identification of the remote.
#[derive(Debug, Clone)]
pub struct IdentifyTransportOutcome {
/// Identification of the remote.
pub info: IdentifyInfo,
/// Address the remote sees for us.
pub observed_addr: Multiaddr,
}
fn cache_entry<F, Fut>(cache: &Mutex<FnvHashMap<Multiaddr, CacheEntry>>, addr: Multiaddr, if_no_entry: F)
-> impl Future<Item = IdentifyTransportOutcome, Error = IoError>
where F: FnOnce() -> Fut,
2018-09-06 09:54:35 +02:00
Fut: Future<Item = IdentifyTransportOutcome, Error = IoError> + Send + 'static,
2018-07-11 14:35:24 +02:00
{
trace!("Looking up cache entry for {}", addr);
let mut cache = cache.lock();
match cache.entry(addr) {
Entry::Occupied(entry) => {
trace!("Cache entry found, cloning");
future::Either::A(entry.get().clone())
},
Entry::Vacant(entry) => {
trace!("No cache entry available");
2018-09-06 09:54:35 +02:00
let future = (Box::new(if_no_entry()) as Box<Future<Item = _, Error = _> + Send>).shared();
2018-07-11 14:35:24 +02:00
entry.insert(future.clone());
future::Either::B(future)
},
2018-07-17 23:26:50 +02:00
}.map(|out| (*out).clone()).map_err(|err| IoError::new(err.kind(), err.description()))
2018-07-11 14:35:24 +02:00
}
// TODO: test that we receive back what the remote sent us