From bd69c0112c3f224e5ef3c9a29cc740d5b5d5a080 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 18 Oct 2018 10:46:36 +0200 Subject: [PATCH 01/23] Add ProtocolsHandler trait --- core/Cargo.toml | 2 +- core/src/lib.rs | 3 +- core/src/nodes/handled_node.rs | 11 + core/src/nodes/mod.rs | 1 + core/src/nodes/protocols_handler.rs | 605 ++++++++++++++++++++++++++++ 5 files changed, 619 insertions(+), 3 deletions(-) create mode 100644 core/src/nodes/protocols_handler.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 048c464e3c7..f979883458f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,6 +20,7 @@ rw-stream-sink = { path = "../misc/rw-stream-sink" } smallvec = "0.6" tokio-executor = "0.1.4" tokio-io = "0.1" +tokio-timer = "0.2" void = "1" [dev-dependencies] @@ -30,6 +31,5 @@ rand = "0.5" tokio = "0.1" tokio-codec = "0.1" tokio-current-thread = "0.1" -tokio-timer = "0.2" assert_matches = "1.3" tokio-mock-task = "0.1" diff --git a/core/src/lib.rs b/core/src/lib.rs index 798405bba50..0e813b6a220 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -182,6 +182,7 @@ extern crate rw_stream_sink; extern crate smallvec; extern crate tokio_executor; extern crate tokio_io; +extern crate tokio_timer; extern crate void; #[cfg(test)] @@ -193,8 +194,6 @@ extern crate tokio_codec; #[cfg(test)] extern crate tokio_current_thread; #[cfg(test)] -extern crate tokio_timer; -#[cfg(test)] #[macro_use] extern crate assert_matches; #[cfg(test)] diff --git a/core/src/nodes/handled_node.rs b/core/src/nodes/handled_node.rs index cc638ab6f26..9767ec25edc 100644 --- a/core/src/nodes/handled_node.rs +++ b/core/src/nodes/handled_node.rs @@ -41,6 +41,12 @@ pub trait NodeHandler { /// Sends a new substream to the handler. /// /// The handler is responsible for upgrading the substream to whatever protocol it wants. + /// + /// # Panic + /// + /// Implementations are allowed to panic in the case of dialing if the `user_data` in + /// `endpoint` doesn't correspond to what was returned earlier when polling, or is used + /// multiple times. fn inject_substream(&mut self, substream: Self::Substream, endpoint: NodeHandlerEndpoint); /// Indicates to the handler that the inbound part of the muxer has been closed, and that @@ -49,6 +55,11 @@ pub trait NodeHandler { /// Indicates to the handler that an outbound substream failed to open because the outbound /// part of the muxer has been closed. + /// + /// # Panic + /// + /// Implementations are allowed to panic if `user_data` doesn't correspond to what was returned + /// earlier when polling, or is used multiple times. fn inject_outbound_closed(&mut self, user_data: Self::OutboundOpenInfo); /// Injects an event coming from the outside into the handler. diff --git a/core/src/nodes/mod.rs b/core/src/nodes/mod.rs index 1d78ea7b3a3..97ae0711d6b 100644 --- a/core/src/nodes/mod.rs +++ b/core/src/nodes/mod.rs @@ -23,6 +23,7 @@ pub mod handled_node; pub mod handled_node_tasks; pub mod listeners; pub mod node; +pub mod protocols_handler; pub mod raw_swarm; pub use self::node::Substream; diff --git a/core/src/nodes/protocols_handler.rs b/core/src/nodes/protocols_handler.rs new file mode 100644 index 00000000000..351531599f4 --- /dev/null +++ b/core/src/nodes/protocols_handler.rs @@ -0,0 +1,605 @@ +// 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 futures::prelude::*; +use nodes::handled_node::{NodeHandler, NodeHandlerEndpoint, NodeHandlerEvent}; +use std::{io, marker::PhantomData, time::Duration}; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_timer::Timeout; +use upgrade::{self, apply::UpgradeApplyFuture, DeniedConnectionUpgrade}; +use void::Void; +use {ConnectionUpgrade, Endpoint}; + +/// Handler for a set of protocols for a specific connection with a remote. +/// +/// This trait should be implemented on struct that hold the state for a specific protocol +/// behaviour with a specific remote. +/// +/// # Handling a protocol +/// +/// Protocols with the remote can be opened in two different ways: +/// +/// - Dialing, which is a voluntary process. In order to do so, make `poll()` return a +/// `OutboundSubstreamRequest` variant containing the connection upgrade to use. +/// - Listening, which is used to determine which protocols are supported when the remote wants +/// to open a substream. The `listen_protocol()` method should return the upgrades supported when +/// listening. +/// +/// The upgrade when dialing and the upgrade when listening have to be of the same type, but you +/// are free to return for example an `OrUpgrade` enum, or an enum of yours, containing the upgrade +/// you want depending on the situation. +/// +/// # Shutting down +/// +/// Implementors of this trait should keep in mind that the connection can be closed at any time. +/// When a connection is closed (either by us or by the remote) `shutdown()` is called and the +/// handler continues to be processed until it produces `None`. Then only the handler is destroyed. +/// +/// This makes it possible for the handler to finish delivering events even after knowing that it +/// is shutting down. +/// +/// Implementors of this trait should keep in mind that when `shutdown()` is called, the connection +/// might already be closed or unresponsive. They should therefore not rely on being able to +/// deliver messages. +/// +/// # Relationship with `NodeHandler`. +/// +/// This trait is very similar to the `NodeHandler` trait. The fundamental differences are: +/// +/// - The `NodeHandler` trait gives you more control and is therefore more difficult to implement. +/// - The `NodeHandler` trait is designed to have exclusive ownership of the connection with a +/// node, while the `ProtocolsHandler` trait is designed to handle only a specific set of +/// protocols. Two or more implementations of `ProtocolsHandler` can be combined into one that +/// supports all the protocols together, which is not possible with `NodeHandler`. +/// +// TODO: add a "blocks connection closing" system, so that we can gracefully close a connection +// when it's no longer needed, and so that for example the periodic pinging system does not +// keep the connection alive forever +pub trait ProtocolsHandler { + /// Custom event that can be received from the outside. + type InEvent; + /// Custom event that can be produced by the handler and that will be returned to the outside. + type OutEvent; + /// The type of the substream that contains the raw data. + type Substream: AsyncRead + AsyncWrite; + /// The upgrade for the protocol or protocols handled by this handler. + type Protocol: ConnectionUpgrade; + /// Information about a substream. Can be sent to the handler through a `NodeHandlerEndpoint`, + /// and will be passed back in `inject_substream` or `inject_outbound_closed`. + type OutboundOpenInfo; + + /// Produces a `ConnectionUpgrade` for the protocol or protocols to accept when listening. + /// + /// > **Note**: You should always accept all the protocols you support, even if in a specific + /// > context you wouldn't accept one in particular (eg. only allow one substream at + /// > a time for a given protocol). The reason is that remotes are allowed to put the + /// > list of supported protocols in a cache in order to avoid spurious queries. + fn listen_protocol(&self) -> Self::Protocol; + + /// Injects a fully-negotiated substream in the handler. + /// + /// This method is called when a substream has been successfully opened and negotiated. + fn inject_fully_negotiated( + &mut self, + protocol: >::Output, + endpoint: NodeHandlerEndpoint, + ); + + /// Injects an event coming from the outside in the handler. + fn inject_event(&mut self, event: &Self::InEvent); + + /// Indicates to the handler that upgrading a substream to the given protocol has failed. + fn inject_dial_upgrade_error(&mut self, info: Self::OutboundOpenInfo, error: io::Error); + + /// Indicates the handler that the inbound part of the muxer has been closed, and that + /// therefore no more inbound substream will be produced. + fn inject_inbound_closed(&mut self); + + /// Indicates the node that it should shut down. After that, it is expected that `poll()` + /// returns `Ready(None)` as soon as possible. + /// + /// This method allows an implementation to perform a graceful shutdown of the substreams, and + /// send back various events. + fn shutdown(&mut self); + + /// Should behave like `Stream::poll()`. Should close if no more event can be produced and the + /// node should be closed. + // TODO: should not return a `NodeHandlerEvent` but a different enum + fn poll( + &mut self, + ) -> Poll< + Option>, + io::Error, + >; + + /// Adds a closure that turns the input event into something else. + #[inline] + fn map_in_event(self, map: TMap) -> MapInEvent + where + Self: Sized, + TMap: Fn(&TNewIn) -> Option<&Self::InEvent>, + { + MapInEvent { + inner: self, + map, + marker: PhantomData, + } + } + + /// Adds a closure that turns the output event into something else. + #[inline] + fn map_out_event(self, map: TMap) -> MapOutEvent + where + Self: Sized, + TMap: FnMut(Self::OutEvent) -> TNewOut, + { + MapOutEvent { inner: self, map } + } + + /// Builds an implementation of `NodeHandler` that handles this protocol exclusively. + #[inline] + fn into_node_handler(self) -> NodeHandlerWrapper + where + Self: Sized, + { + NodeHandlerWrapper { + handler: self, + negotiating_in: Vec::new(), + negotiating_out: Vec::new(), + in_timeout: Duration::from_secs(10), + out_timeout: Duration::from_secs(10), + queued_dial_upgrades: Vec::new(), + unique_dial_upgrade_id: 0, + } + } +} + +/// Event produced by a handler. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ProtocolsHandlerEvent { + /// Require a new outbound substream to be opened with the remote. + OutboundSubstreamRequest { + /// The upgrade to apply on the substream. + upgrade: TConnectionUpgrade, + /// User-defind information, passed back when the substream is open. + info: TOutboundOpenInfo, + }, + + /// Other event. + Custom(TCustom), +} + +/// Event produced by a handler. +impl + ProtocolsHandlerEvent +{ + /// If this is `OutboundSubstreamRequest`, maps the content to something else. + #[inline] + pub fn map_outbound_open_info( + self, + map: F, + ) -> ProtocolsHandlerEvent + where + F: FnOnce(TOutboundOpenInfo) -> I, + { + match self { + ProtocolsHandlerEvent::OutboundSubstreamRequest { upgrade, info } => { + ProtocolsHandlerEvent::OutboundSubstreamRequest { + upgrade, + info: map(info), + } + } + ProtocolsHandlerEvent::Custom(val) => ProtocolsHandlerEvent::Custom(val), + } + } + + /// If this is `Custom`, maps the content to something else. + #[inline] + pub fn map_custom( + self, + map: F, + ) -> ProtocolsHandlerEvent + where + F: FnOnce(TCustom) -> I, + { + match self { + ProtocolsHandlerEvent::OutboundSubstreamRequest { upgrade, info } => { + ProtocolsHandlerEvent::OutboundSubstreamRequest { upgrade, info } + } + ProtocolsHandlerEvent::Custom(val) => ProtocolsHandlerEvent::Custom(map(val)), + } + } +} + +/// Implementation of `ProtocolsHandler` that doesn't handle anything. +pub struct DummyProtocolsHandler { + shutting_down: bool, + marker: PhantomData, +} + +impl Default for DummyProtocolsHandler { + #[inline] + fn default() -> Self { + DummyProtocolsHandler { + shutting_down: false, + marker: PhantomData, + } + } +} + +impl ProtocolsHandler for DummyProtocolsHandler +where + TSubstream: AsyncRead + AsyncWrite, +{ + type InEvent = Void; + type OutEvent = Void; + type Substream = TSubstream; + type Protocol = DeniedConnectionUpgrade; + type OutboundOpenInfo = Void; + + #[inline] + fn listen_protocol(&self) -> Self::Protocol { + DeniedConnectionUpgrade + } + + #[inline] + fn inject_fully_negotiated( + &mut self, + _: >::Output, + _: NodeHandlerEndpoint, + ) { + } + + #[inline] + fn inject_event(&mut self, _: &Self::InEvent) {} + + #[inline] + fn inject_dial_upgrade_error(&mut self, _: Self::OutboundOpenInfo, _: io::Error) {} + + #[inline] + fn inject_inbound_closed(&mut self) {} + + #[inline] + fn shutdown(&mut self) { + self.shutting_down = true; + } + + #[inline] + fn poll( + &mut self, + ) -> Poll< + Option>, + io::Error, + > { + if self.shutting_down { + Ok(Async::Ready(None)) + } else { + Ok(Async::NotReady) + } + } +} + +/// Wrapper around a protocol handler that turns the input event into something else. +pub struct MapInEvent { + inner: TProtoHandler, + map: TMap, + marker: PhantomData, +} + +impl ProtocolsHandler for MapInEvent +where + TProtoHandler: ProtocolsHandler, + TMap: Fn(&TNewIn) -> Option<&TProtoHandler::InEvent>, +{ + type InEvent = TNewIn; + type OutEvent = TProtoHandler::OutEvent; + type Substream = TProtoHandler::Substream; + type Protocol = TProtoHandler::Protocol; + type OutboundOpenInfo = TProtoHandler::OutboundOpenInfo; + + #[inline] + fn listen_protocol(&self) -> Self::Protocol { + self.inner.listen_protocol() + } + + #[inline] + fn inject_fully_negotiated( + &mut self, + protocol: >::Output, + endpoint: NodeHandlerEndpoint, + ) { + self.inner.inject_fully_negotiated(protocol, endpoint) + } + + #[inline] + fn inject_event(&mut self, event: &TNewIn) { + if let Some(event) = (self.map)(event) { + self.inner.inject_event(event); + } + } + + #[inline] + fn inject_dial_upgrade_error(&mut self, info: Self::OutboundOpenInfo, error: io::Error) { + self.inner.inject_dial_upgrade_error(info, error) + } + + #[inline] + fn inject_inbound_closed(&mut self) { + self.inner.inject_inbound_closed() + } + + #[inline] + fn shutdown(&mut self) { + self.inner.shutdown() + } + + #[inline] + fn poll( + &mut self, + ) -> Poll< + Option>, + io::Error, + > { + self.inner.poll() + } +} + +/// Wrapper around a protocol handler that turns the output event into something else. +pub struct MapOutEvent { + inner: TProtoHandler, + map: TMap, +} + +impl ProtocolsHandler for MapOutEvent +where + TProtoHandler: ProtocolsHandler, + TMap: FnMut(TProtoHandler::OutEvent) -> TNewOut, +{ + type InEvent = TProtoHandler::InEvent; + type OutEvent = TNewOut; + type Substream = TProtoHandler::Substream; + type Protocol = TProtoHandler::Protocol; + type OutboundOpenInfo = TProtoHandler::OutboundOpenInfo; + + #[inline] + fn listen_protocol(&self) -> Self::Protocol { + self.inner.listen_protocol() + } + + #[inline] + fn inject_fully_negotiated( + &mut self, + protocol: >::Output, + endpoint: NodeHandlerEndpoint, + ) { + self.inner.inject_fully_negotiated(protocol, endpoint) + } + + #[inline] + fn inject_event(&mut self, event: &Self::InEvent) { + self.inner.inject_event(event) + } + + #[inline] + fn inject_dial_upgrade_error(&mut self, info: Self::OutboundOpenInfo, error: io::Error) { + self.inner.inject_dial_upgrade_error(info, error) + } + + #[inline] + fn inject_inbound_closed(&mut self) { + self.inner.inject_inbound_closed() + } + + #[inline] + fn shutdown(&mut self) { + self.inner.shutdown() + } + + #[inline] + fn poll( + &mut self, + ) -> Poll< + Option>, + io::Error, + > { + Ok(self.inner.poll()?.map(|ev| { + ev.map(|ev| match ev { + ProtocolsHandlerEvent::Custom(ev) => ProtocolsHandlerEvent::Custom((self.map)(ev)), + ProtocolsHandlerEvent::OutboundSubstreamRequest { upgrade, info } => { + ProtocolsHandlerEvent::OutboundSubstreamRequest { upgrade, info } + } + }) + })) + } +} + +/// Wraps around an implementation of `ProtocolsHandler`, and implements `NodeHandler`. +// TODO: add a caching system for protocols that are supported or not +pub struct NodeHandlerWrapper +where + TProtoHandler: ProtocolsHandler, +{ + /// The underlying handler. + handler: TProtoHandler, + /// Futures that upgrade incoming substreams. + negotiating_in: + Vec>>, + /// Futures that upgrade outgoing substreams. The first element of the tuple is the userdata + /// to pass back once successfully opened. + negotiating_out: Vec<( + TProtoHandler::OutboundOpenInfo, + Timeout>, + )>, + /// Timeout for incoming substreams negotiation. + in_timeout: Duration, + /// Timeout for outgoing substreams negotiation. + out_timeout: Duration, + /// For each outbound substream request, how to upgrade it. The first element of the tuple + /// is the unique identifier (see `unique_dial_upgrade_id`). + queued_dial_upgrades: Vec<(u64, TProtoHandler::Protocol)>, + /// Unique identifier assigned to each queued dial upgrade. + unique_dial_upgrade_id: u64, +} + +impl NodeHandler for NodeHandlerWrapper +where + TProtoHandler: ProtocolsHandler, + >::NamesIter: Clone, +{ + type InEvent = TProtoHandler::InEvent; + type OutEvent = TProtoHandler::OutEvent; + type Substream = TProtoHandler::Substream; + // The first element of the tuple is the unique upgrade identifier + // (see `unique_dial_upgrade_id`). + type OutboundOpenInfo = (u64, TProtoHandler::OutboundOpenInfo); + + fn inject_substream( + &mut self, + substream: Self::Substream, + endpoint: NodeHandlerEndpoint, + ) { + match endpoint { + NodeHandlerEndpoint::Listener => { + let protocol = self.handler.listen_protocol(); + let upgrade = upgrade::apply(substream, protocol, Endpoint::Listener); + let with_timeout = Timeout::new(upgrade, self.in_timeout); + self.negotiating_in.push(with_timeout); + } + NodeHandlerEndpoint::Dialer((upgrade_id, user_data)) => { + let pos = match self + .queued_dial_upgrades + .iter() + .position(|(id, _)| id == &upgrade_id) + { + Some(p) => p, + None => { + debug_assert!(false, "Received an upgrade with an invalid upgrade ID"); + return; + } + }; + + let (_, proto_upgrade) = self.queued_dial_upgrades.remove(pos); + let upgrade = upgrade::apply(substream, proto_upgrade, Endpoint::Dialer); + let with_timeout = Timeout::new(upgrade, self.out_timeout); + self.negotiating_out.push((user_data, with_timeout)); + } + } + } + + #[inline] + fn inject_inbound_closed(&mut self) { + self.handler.inject_inbound_closed(); + } + + fn inject_outbound_closed(&mut self, user_data: Self::OutboundOpenInfo) { + let pos = match self + .queued_dial_upgrades + .iter() + .position(|(id, _)| id == &user_data.0) + { + Some(p) => p, + None => { + debug_assert!( + false, + "Received an outbound closed error with an invalid upgrade ID" + ); + return; + } + }; + + self.queued_dial_upgrades.remove(pos); + self.handler + .inject_dial_upgrade_error(user_data.1, io::ErrorKind::ConnectionReset.into()); + } + + #[inline] + fn inject_event(&mut self, event: Self::InEvent) { + self.handler.inject_event(&event); + } + + #[inline] + fn shutdown(&mut self) { + self.handler.shutdown(); + } + + fn poll( + &mut self, + ) -> Poll>, io::Error> { + // Continue negotiation of newly-opened substreams on the listening side. + // We remove each element from `negotiating_in` one by one and add them back if not ready. + for n in (0..self.negotiating_in.len()).rev() { + let mut in_progress = self.negotiating_in.swap_remove(n); + match in_progress.poll() { + Ok(Async::Ready(upgrade)) => { + self.handler + .inject_fully_negotiated(upgrade, NodeHandlerEndpoint::Listener); + } + Ok(Async::NotReady) => { + self.negotiating_in.push(in_progress); + } + // TODO: return a diagnostic event? + Err(_err) => {} + } + } + + // Continue negotiation of newly-opened substreams. + // We remove each element from `negotiating_out` one by one and add them back if not ready. + for n in (0..self.negotiating_out.len()).rev() { + let (upgr_info, mut in_progress) = self.negotiating_out.swap_remove(n); + match in_progress.poll() { + Ok(Async::Ready(upgrade)) => { + let endpoint = NodeHandlerEndpoint::Dialer(upgr_info); + self.handler.inject_fully_negotiated(upgrade, endpoint); + } + Ok(Async::NotReady) => { + self.negotiating_out.push((upgr_info, in_progress)); + } + Err(err) => { + let msg = format!("Error while upgrading: {:?}", err); + let err = io::Error::new(io::ErrorKind::Other, msg); + self.handler.inject_dial_upgrade_error(upgr_info, err); + } + } + } + + // Poll the handler at the end so that we see the consequences of the method calls on + // `self.handler`. + match self.handler.poll()? { + Async::Ready(Some(ProtocolsHandlerEvent::Custom(event))) => { + return Ok(Async::Ready(Some(NodeHandlerEvent::Custom(event)))); + } + Async::Ready(Some(ProtocolsHandlerEvent::OutboundSubstreamRequest { + upgrade, + info, + })) => { + let id = self.unique_dial_upgrade_id; + self.unique_dial_upgrade_id += 1; + self.queued_dial_upgrades.push((id, upgrade)); + return Ok(Async::Ready(Some( + NodeHandlerEvent::OutboundSubstreamRequest((id, info)), + ))); + } + Async::Ready(None) => return Ok(Async::Ready(None)), + Async::NotReady => (), + }; + + Ok(Async::NotReady) + } +} From d283c01e1088af1666bb1b500838ab25aafc642e Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 18 Oct 2018 15:03:05 +0200 Subject: [PATCH 02/23] Reexport symbols --- core/src/nodes/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/nodes/mod.rs b/core/src/nodes/mod.rs index 97ae0711d6b..2636de3567a 100644 --- a/core/src/nodes/mod.rs +++ b/core/src/nodes/mod.rs @@ -28,4 +28,5 @@ pub mod raw_swarm; pub use self::node::Substream; pub use self::handled_node::{NodeHandlerEvent, NodeHandlerEndpoint}; +pub use self::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; pub use self::raw_swarm::{ConnectedPoint, Peer, RawSwarm, RawSwarmEvent}; From 20dae459a42652c8884138f7534d59e4aa59b2d6 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 18 Oct 2018 15:06:25 +0200 Subject: [PATCH 03/23] Add a note about shutting down --- core/src/nodes/protocols_handler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/nodes/protocols_handler.rs b/core/src/nodes/protocols_handler.rs index 351531599f4..ff791ffc01f 100644 --- a/core/src/nodes/protocols_handler.rs +++ b/core/src/nodes/protocols_handler.rs @@ -121,7 +121,10 @@ pub trait ProtocolsHandler { /// Should behave like `Stream::poll()`. Should close if no more event can be produced and the /// node should be closed. - // TODO: should not return a `NodeHandlerEvent` but a different enum + /// + /// > **Note**: If this handler is combined with other handlers, as soon as `poll()` returns + /// > `Ok(Async::Ready(None))`, all the other handlers will receive a call to + /// > `shutdown()` and will eventually be closed and destroyed. fn poll( &mut self, ) -> Poll< From 27fdfd06a0bd392e9083ed098dadac0387dc0ce2 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 14:26:04 +0200 Subject: [PATCH 04/23] Add some comments to the relay transport --- transports/relay/src/protocol.rs | 37 ++++++++++++++++++++++--------- transports/relay/src/transport.rs | 12 +++++++++- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 984ce4d764d..843ce11590b 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -28,25 +28,35 @@ use std::{io, iter, ops::Deref}; use tokio_io::{AsyncRead, AsyncWrite}; use utility::{io_err, is_success, status, Io, Peer}; +/// Configuration for a connection upgrade that handles the relay protocol. #[derive(Debug, Clone)] pub struct RelayConfig { + /// Peer id of the local node. my_id: PeerId, + /// When asked to relay a connection, the transport to use to reach the requested node. transport: T, + /// Reference to a peerstore to load peers from when the target address is not specified in + /// the destination. peers: P, - // If `allow_relays` is false this node can only be used as a - // destination but will not allow relaying streams to other - // destinations. + /// If `allow_relays` is false this node can only be used as a destination but will not allow + /// relaying streams to other destinations. allow_relays: bool } -// The `RelayConfig` upgrade can serve as destination or relay. Each mode needs a different -// output type. As destination we want the stream to continue to be usable, whereas as relay -// we pipe data from source to destination and do not want to use the stream in any other way. -// Therefore, in the latter case we simply return a future that can be driven to completion -// but otherwise the stream is not programmatically accessible. +/// The `RelayConfig` upgrade can serve as destination or relay. Each mode needs a different +/// output type. As destination we want the stream to continue to be usable, whereas as relay +/// we pipe data from source to destination and do not want to use the stream in any other way. +/// Therefore, in the latter case we simply return a future that can be driven to completion +/// but otherwise the stream is not programmatically accessible. pub enum Output { + /// The source is a relay `R` that relays the communication from someone `A` to us. Keep in + /// mind that the multiaddress you have about this communication is the address of `R`. + // TODO: provide a way to get the address of `A` Stream(C), - Sealed(Box + Send>) + + /// We have been asked to relay communications to another node. Polling the future until it's + /// ready will process the proxying. + Sealed(Box + Send>), } impl ConnectionUpgrade for RelayConfig @@ -106,15 +116,17 @@ where P: Deref + Clone + 'static, for<'a> &'a S: Peerstore, { + /// Builds a new `RelayConfig` with default options. pub fn new(my_id: PeerId, transport: T, peers: P) -> RelayConfig { RelayConfig { my_id, transport, peers, allow_relays: true } } + /// Sets whether we will allow requests for relaying connections. pub fn allow_relays(&mut self, val: bool) { self.allow_relays = val } - // HOP message handling (relay mode). + /// HOP message handling (request to act as a relay). fn on_hop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error=io::Error> where C: AsyncRead + AsyncWrite + 'static, @@ -202,7 +214,7 @@ where B(B(future)) } - // STOP message handling (destination mode) + /// STOP message handling (we are a destination) fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future where C: AsyncRead + AsyncWrite + 'static, @@ -244,6 +256,7 @@ fn stop_message(from: &Peer, dest: &Peer) -> CircuitRelay { msg } +/// Dummy connection upgrade that negotiates the relay protocol and returns the socket. #[derive(Debug, Clone)] struct TrivialUpgrade; @@ -266,6 +279,8 @@ where } } +/// Connection upgrade that negotiates the relay protocol, then negotiates with the target for it +/// to act as destination. #[derive(Debug, Clone)] pub(crate) struct Source(pub(crate) CircuitRelay); diff --git a/transports/relay/src/transport.rs b/transports/relay/src/transport.rs index 0e216b8506d..2b6f0b20ea9 100644 --- a/transports/relay/src/transport.rs +++ b/transports/relay/src/transport.rs @@ -29,12 +29,22 @@ use std::{io, iter::FromIterator, ops::Deref, sync::Arc}; use tokio_io::{AsyncRead, AsyncWrite}; use utility::{io_err, Peer, RelayAddr}; +/// Implementation of `Transport` that supports addresses of the form +/// `/p2p-circuit/`. +/// +/// `` is the address of a node that should act as a relay, and `` is the address +/// of the destination, that the relay should dial. #[derive(Debug, Clone)] pub struct RelayTransport { + /// Id of the local node. my_id: PeerId, + /// Transport to use to. transport: T, + /// Reference to a peerstore that associates the elements in `self.relays` to their + /// multiaddresses. peers: P, - relays: Arc> + /// List of known IDs of peers. + relays: Arc>, } impl Transport for RelayTransport From 462b0cee25a3851b1bf60510bbab4c8849006b84 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 14:36:43 +0200 Subject: [PATCH 05/23] Add the source peer id when we are a destination --- transports/relay/src/protocol.rs | 33 ++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 843ce11590b..faec840a234 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -48,11 +48,16 @@ pub struct RelayConfig { /// we pipe data from source to destination and do not want to use the stream in any other way. /// Therefore, in the latter case we simply return a future that can be driven to completion /// but otherwise the stream is not programmatically accessible. -pub enum Output { - /// The source is a relay `R` that relays the communication from someone `A` to us. Keep in +pub enum Output { + /// The source is a relay `R` that relays the communication from someone `S` to us. Keep in /// mind that the multiaddress you have about this communication is the address of `R`. - // TODO: provide a way to get the address of `A` - Stream(C), + Stream { + /// Stream of data to the original source. + stream: TStream, + /// Identifier of the original source `S`. + src_peer_id: PeerId, + // TODO: also provide the addresses ; however the semantics of these addresses is uncertain + }, /// We have been asked to relay communications to another node. Polling the future until it's /// ready will process the proxying. @@ -93,7 +98,7 @@ where B(A(self.on_hop(msg, io).map(|fut| Output::Sealed(Box::new(fut))))) } CircuitRelay_Type::STOP => { // act as destination - B(B(self.on_stop(msg, io).map(Output::Stream))) + B(B(self.on_stop(msg, io))) } other => { debug!("invalid message type: {:?}", other); @@ -215,15 +220,22 @@ where } /// STOP message handling (we are a destination) - fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future + fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error = io::Error> where C: AsyncRead + AsyncWrite + 'static, { + let from = if let Some(peer) = Peer::from_message(msg.take_srcPeer()) { + peer + } else { + let msg = status(CircuitRelay_Status::HOP_SRC_MULTIADDR_INVALID); + return A(A(io.send(msg).and_then(|_| Err(io_err("invalid src address"))))) + }; + let dest = if let Some(peer) = Peer::from_message(msg.take_dstPeer()) { peer } else { let msg = status(CircuitRelay_Status::STOP_DST_MULTIADDR_INVALID); - return A(io.send(msg).and_then(|_| Err(io_err("invalid dest address")))) + return A(B(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) }; if dest.id != self.my_id { @@ -231,7 +243,12 @@ where return B(A(io.send(msg).and_then(|_| Err(io_err("destination id mismatch"))))) } - B(B(io.send(status(CircuitRelay_Status::SUCCESS)).map(Io::into))) + B(B(io.send(status(CircuitRelay_Status::SUCCESS)).map(move |stream| { + Output::Stream { + stream: stream.into(), + src_peer_id: from.id, + } + }))) } } From 6280294f5b5ff79e18855f1a531a82adb85429d7 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 14:43:19 +0200 Subject: [PATCH 06/23] Add small TODO --- transports/relay/src/protocol.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index faec840a234..6c4fafd1acb 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -61,6 +61,7 @@ pub enum Output { /// We have been asked to relay communications to another node. Polling the future until it's /// ready will process the proxying. + // TODO: provide more info for informative purposes for the user Sealed(Box + Send>), } From 0310f0fd3fc48f5eee3754e560c91054e1752c17 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 14:59:11 +0200 Subject: [PATCH 07/23] Remove the auto relay addresses system --- transports/relay/Cargo.toml | 1 - transports/relay/src/lib.rs | 1 - transports/relay/src/protocol.rs | 32 +++------- transports/relay/src/transport.rs | 97 ++++--------------------------- transports/relay/src/utility.rs | 12 ++-- 5 files changed, 24 insertions(+), 119 deletions(-) diff --git a/transports/relay/Cargo.toml b/transports/relay/Cargo.toml index 0fa426b2b5f..1201d830963 100644 --- a/transports/relay/Cargo.toml +++ b/transports/relay/Cargo.toml @@ -7,7 +7,6 @@ license = "MIT" [dependencies] bytes = "0.4" futures = "0.1" -libp2p-peerstore = { path = "../../stores/peerstore" } libp2p-core = { path = "../../core" } log = "0.4" multiaddr = { path = "../../misc/multiaddr" } diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index 495ad305cfd..e386b715839 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -21,7 +21,6 @@ extern crate bytes; #[macro_use] extern crate futures; -extern crate libp2p_peerstore as peerstore; extern crate libp2p_core as core; #[macro_use] extern crate log; diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 6c4fafd1acb..e1f7998ebfc 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -20,24 +20,20 @@ use bytes::Bytes; use copy; -use core::{ConnectionUpgrade, Endpoint, Transport}; +use core::{ConnectionUpgrade, Endpoint, PeerId, Transport}; use futures::{stream, future::{self, Either::{A, B}, FutureResult}, prelude::*}; use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; -use peerstore::{PeerAccess, PeerId, Peerstore}; -use std::{io, iter, ops::Deref}; +use std::{io, iter}; use tokio_io::{AsyncRead, AsyncWrite}; use utility::{io_err, is_success, status, Io, Peer}; /// Configuration for a connection upgrade that handles the relay protocol. #[derive(Debug, Clone)] -pub struct RelayConfig { +pub struct RelayConfig { /// Peer id of the local node. my_id: PeerId, /// When asked to relay a connection, the transport to use to reach the requested node. transport: T, - /// Reference to a peerstore to load peers from when the target address is not specified in - /// the destination. - peers: P, /// If `allow_relays` is false this node can only be used as a destination but will not allow /// relaying streams to other destinations. allow_relays: bool @@ -65,7 +61,7 @@ pub enum Output { Sealed(Box + Send>), } -impl ConnectionUpgrade for RelayConfig +impl ConnectionUpgrade for RelayConfig where C: AsyncRead + AsyncWrite + Send + 'static, T: Transport + Clone + Send + 'static, @@ -73,9 +69,6 @@ where T::Listener: Send, T::ListenerUpgrade: Send, T::Output: AsyncRead + AsyncWrite + Send, - P: Deref + Clone + Send + 'static, - S: 'static, - for<'a> &'a S: Peerstore { type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>; type UpgradeIdentifier = (); @@ -112,19 +105,17 @@ where } } -impl RelayConfig +impl RelayConfig where T: Transport + Clone + 'static, T::Dial: Send, // TODO: remove T::Listener: Send, // TODO: remove T::ListenerUpgrade: Send, // TODO: remove T::Output: Send + AsyncRead + AsyncWrite, - P: Deref + Clone + 'static, - for<'a> &'a S: Peerstore, { /// Builds a new `RelayConfig` with default options. - pub fn new(my_id: PeerId, transport: T, peers: P) -> RelayConfig { - RelayConfig { my_id, transport, peers, allow_relays: true } + pub fn new(my_id: PeerId, transport: T) -> RelayConfig { + RelayConfig { my_id, transport, allow_relays: true } } /// Sets whether we will allow requests for relaying connections. @@ -144,20 +135,13 @@ where return A(io.send(msg).and_then(|_| Err(io_err("invalid src address")))) }; - let mut dest = if let Some(peer) = Peer::from_message(msg.take_dstPeer()) { + let dest = if let Some(peer) = Peer::from_message(msg.take_dstPeer()) { peer } else { let msg = status(CircuitRelay_Status::HOP_DST_MULTIADDR_INVALID); return B(A(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) }; - if dest.addrs.is_empty() { - // Add locally know addresses of destination - if let Some(peer) = self.peers.peer(&dest.id) { - dest.addrs.extend(peer.addrs()) - } - } - let stop = stop_message(&from, &dest); let transport = self.transport.with_upgrade(TrivialUpgrade); diff --git a/transports/relay/src/transport.rs b/transports/relay/src/transport.rs index 2b6f0b20ea9..6d9882c1db4 100644 --- a/transports/relay/src/transport.rs +++ b/transports/relay/src/transport.rs @@ -18,14 +18,12 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use core::Transport; +use core::{PeerId, Transport}; use futures::{stream, prelude::*}; use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Type}; use multiaddr::Multiaddr; -use peerstore::{PeerAccess, PeerId, Peerstore}; use protocol; -use rand::{self, Rng}; -use std::{io, iter::FromIterator, ops::Deref, sync::Arc}; +use std::io; use tokio_io::{AsyncRead, AsyncWrite}; use utility::{io_err, Peer, RelayAddr}; @@ -35,28 +33,20 @@ use utility::{io_err, Peer, RelayAddr}; /// `` is the address of a node that should act as a relay, and `` is the address /// of the destination, that the relay should dial. #[derive(Debug, Clone)] -pub struct RelayTransport { +pub struct RelayTransport { /// Id of the local node. my_id: PeerId, /// Transport to use to. transport: T, - /// Reference to a peerstore that associates the elements in `self.relays` to their - /// multiaddresses. - peers: P, - /// List of known IDs of peers. - relays: Arc>, } -impl Transport for RelayTransport +impl Transport for RelayTransport where T: Transport + Send + Clone + 'static, T::Dial: Send, T::Listener: Send, T::ListenerUpgrade: Send, T::Output: AsyncRead + AsyncWrite + Send, - P: Deref + Clone + 'static, - S: 'static, - for<'a> &'a S: Peerstore { type Output = T::Output; type Listener = Box + Send>; @@ -78,13 +68,8 @@ where return Err((self, addr)); } RelayAddr::Address { relay, dest } => { - if let Some(ref r) = relay { - let f = self.relay_via(r, &dest).map_err(|this| (this, addr))?; - Ok(Box::new(f)) - } else { - let f = self.relay_to(&dest).map_err(|this| (this, addr))?; - Ok(Box::new(f)) - } + let f = self.relay_via(&relay, &dest).map_err(|this| (this, addr))?; + Ok(Box::new(f)) } } } @@ -94,87 +79,31 @@ where } } -impl RelayTransport +impl RelayTransport where T: Transport + Clone + 'static, T::Dial: Send, T::Listener: Send, T::ListenerUpgrade: Send, T::Output: AsyncRead + AsyncWrite + Send, - P: Deref + Clone + 'static, - for<'a> &'a S: Peerstore { /// Create a new relay transport. /// /// This transport uses a static set of relays and will not attempt /// any relay discovery. - pub fn new(my_id: PeerId, transport: T, peers: P, relays: R) -> Self - where - R: IntoIterator, - { + pub fn new(my_id: PeerId, transport: T) -> Self { RelayTransport { my_id, transport, - peers, - relays: Arc::new(Vec::from_iter(relays)), } } - // Relay to destination over any available relay node. - fn relay_to(self, destination: &Peer) -> Result, Self> { - trace!("relay_to {:?}", destination.id); - let mut dials = Vec::new(); - for relay in &*self.relays { - let relay_peer = Peer { - id: relay.clone(), - addrs: Vec::new(), - }; - if let Ok(dial) = self.clone().relay_via(&relay_peer, destination) { - dials.push(dial) - } - } - - if dials.is_empty() { - info!("no relay available for {:?}", destination.id); - return Err(self); - } - - // Try one relay after another and stick to the first working one. - rand::thread_rng().shuffle(&mut dials); // randomise to spread load - let dest_peer = destination.id.clone(); - let future = stream::iter_ok(dials.into_iter()) - .and_then(|dial| dial) - .then(|result| Ok(result.ok())) - .filter_map(|result| result) - .into_future() - .map_err(|(err, _stream)| err) - .and_then(move |(ok, _stream)| { - if let Some(out) = ok { - Ok(out) - } else { - Err(io_err(format!("no relay for {:?}", dest_peer))) - } - }); - Ok(future) - } - // Relay to destination via the given peer. fn relay_via(self, relay: &Peer, destination: &Peer) -> Result, Self> { trace!("relay_via {:?} to {:?}", relay.id, destination.id); - let mut addresses = Vec::new(); - - if relay.addrs.is_empty() { - // try all known relay addresses - if let Some(peer) = self.peers.peer(&relay.id) { - addresses.extend(peer.addrs()) - } - } else { - // use only specific relay addresses - addresses.extend(relay.addrs.iter().cloned()) - } // no relay address => bail out - if addresses.is_empty() { + if relay.addrs.is_empty() { info!("no available address for relay: {:?}", relay.id); return Err(self); } @@ -182,7 +111,7 @@ where let relay = relay.clone(); let message = self.hop_message(destination); let transport = self.transport.with_upgrade(protocol::Source(message)); - let future = stream::iter_ok(addresses.into_iter()) + let future = stream::iter_ok(relay.addrs.clone().into_iter()) .filter_map(move |addr| transport.clone().dial(addr).ok()) .and_then(|dial| dial) .then(|result| Ok(result.ok())) @@ -208,11 +137,7 @@ where let mut from = CircuitRelay_Peer::new(); from.set_id(self.my_id.as_bytes().to_vec()); - if let Some(me) = self.peers.peer(&self.my_id) { - for a in me.addrs() { - from.mut_addrs().push(a.to_bytes()) - } - } + // TODO: fill from.mut_addrs() msg.set_srcPeer(from); let mut dest = CircuitRelay_Peer::new(); diff --git a/transports/relay/src/utility.rs b/transports/relay/src/utility.rs index bb201335554..a6805e49195 100644 --- a/transports/relay/src/utility.rs +++ b/transports/relay/src/utility.rs @@ -18,10 +18,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use core::PeerId; use futures::{future::{self, Either}, prelude::*}; use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; use multiaddr::{Protocol, Multiaddr}; -use peerstore::PeerId; use protobuf::{self, Message}; use std::{io, error::Error, iter::FromIterator}; use tokio_codec::Framed; @@ -93,7 +93,7 @@ where } pub(crate) enum RelayAddr { - Address { relay: Option, dest: Peer }, + Address { relay: Peer, dest: Peer }, Malformed, Multihop, // Unsupported } @@ -101,15 +101,13 @@ pub(crate) enum RelayAddr { impl RelayAddr { // Address format: []/p2p-circuit/ pub(crate) fn parse(addr: &Multiaddr) -> RelayAddr { - let mut iter = addr.iter().peekable(); + let mut iter = addr.iter(); - let relay = if let Some(&Protocol::P2pCircuit) = iter.peek() { - None // Address begins with "p2p-circuit", i.e. no relay is specified. - } else { + let relay = { let prefix = iter.by_ref().take_while(|p| *p != Protocol::P2pCircuit); match Peer::from(Multiaddr::from_iter(prefix)) { None => return RelayAddr::Malformed, - peer => peer, + Some(peer) => peer, } }; From 48ba07364ff4bbf83c11cb245fef20349c0eb08e Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 15:02:50 +0200 Subject: [PATCH 08/23] Rename Output to RelayOutput --- transports/relay/src/lib.rs | 2 +- transports/relay/src/protocol.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index e386b715839..9a988af0483 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -37,5 +37,5 @@ mod protocol; mod transport; mod utility; -pub use protocol::{Output, RelayConfig}; +pub use protocol::{RelayOutput, RelayConfig}; pub use transport::RelayTransport; diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index e1f7998ebfc..6e514175e28 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -44,7 +44,7 @@ pub struct RelayConfig { /// we pipe data from source to destination and do not want to use the stream in any other way. /// Therefore, in the latter case we simply return a future that can be driven to completion /// but otherwise the stream is not programmatically accessible. -pub enum Output { +pub enum RelayOutput { /// The source is a relay `R` that relays the communication from someone `S` to us. Keep in /// mind that the multiaddress you have about this communication is the address of `R`. Stream { @@ -77,7 +77,7 @@ where iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) } - type Output = Output; + type Output = RelayOutput; type Future = Box + Send>; fn upgrade(self, conn: C, _: (), _: Endpoint) -> Self::Future { @@ -89,7 +89,7 @@ where }; match msg.get_field_type() { CircuitRelay_Type::HOP if self.allow_relays => { // act as relay - B(A(self.on_hop(msg, io).map(|fut| Output::Sealed(Box::new(fut))))) + B(A(self.on_hop(msg, io).map(|fut| RelayOutput::Sealed(Box::new(fut))))) } CircuitRelay_Type::STOP => { // act as destination B(B(self.on_stop(msg, io))) @@ -205,7 +205,7 @@ where } /// STOP message handling (we are a destination) - fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error = io::Error> + fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error = io::Error> where C: AsyncRead + AsyncWrite + 'static, { @@ -229,7 +229,7 @@ where } B(B(io.send(status(CircuitRelay_Status::SUCCESS)).map(move |stream| { - Output::Stream { + RelayOutput::Stream { stream: stream.into(), src_peer_id: from.id, } From ac46279ae84dfc9a496864edf272141d58d2ad38 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 15:49:59 +0200 Subject: [PATCH 09/23] Add RelayHandler --- transports/relay/Cargo.toml | 1 + transports/relay/src/handler.rs | 160 ++++++++++++++++++++++++++++++++ transports/relay/src/lib.rs | 3 + 3 files changed, 164 insertions(+) create mode 100644 transports/relay/src/handler.rs diff --git a/transports/relay/Cargo.toml b/transports/relay/Cargo.toml index 1201d830963..0b62261c2c3 100644 --- a/transports/relay/Cargo.toml +++ b/transports/relay/Cargo.toml @@ -15,3 +15,4 @@ rand = "0.4" tokio-codec = "0.1" tokio-io = "0.1" unsigned-varint = { version = "0.2.1", features = ["codec"] } +void = "1.0" diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs new file mode 100644 index 00000000000..b9621bb27d4 --- /dev/null +++ b/transports/relay/src/handler.rs @@ -0,0 +1,160 @@ +// 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 futures::prelude::*; +use core::{PeerId, Transport}; +use core::nodes::handled_node::NodeHandlerEndpoint; +use core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; +use core::ConnectionUpgrade; +use protocol::{RelayOutput, RelayConfig}; +use std::io; +use tokio_io::{AsyncRead, AsyncWrite}; +use void::Void; + +/// Protocol handler that identifies the remote at a regular period. +pub struct RelayHandler { + /// Configuration for the protocol. + config: RelayConfig, + + /// True if wanting to shut down. + shutdown: bool, + + /// List of futures that process relaying data from the remote node to a destination. + active_relays: Vec>>, + + /// Queue of events to return when polled. + queued_events: Vec>, +} + +/// Event produced by the periodic identifier. +#[derive(Debug)] +pub enum RelayHandlerEvent { + /// The remote is a relay and is relaying a connection to us. In other words, we are used as + /// destination. + Destination { + /// I/O to the source. + stream: TSubstream, + /// Peer id of the source. + src_peer_id: PeerId, + }, +} + +impl RelayHandler +where TTrans: Transport + Clone + 'static, + TTrans::Dial: Send, // TODO: remove + TTrans::Listener: Send, // TODO: remove + TTrans::ListenerUpgrade: Send, // TODO: remove + TTrans::Output: Send + AsyncRead + AsyncWrite, +{ + /// Builds a new `RelayHandler`. + #[inline] + pub fn new(my_id: PeerId, transport: TTrans) -> Self { + RelayHandler { + config: RelayConfig::new(my_id, transport), + shutdown: false, + active_relays: Vec::new(), + queued_events: Vec::new(), + } + } +} + +impl ProtocolsHandler for RelayHandler +where + TTrans: Transport + Clone + Send + Sync + 'static, + TTrans::Dial: Send, + TTrans::Listener: Send, + TTrans::ListenerUpgrade: Send, + TTrans::Output: AsyncRead + AsyncWrite + Send, + TSubstream: AsyncRead + AsyncWrite + Send + Sync + 'static, // TODO: remove useless bounds +{ + type InEvent = Void; + type OutEvent = RelayHandlerEvent; + type Substream = TSubstream; + type Protocol = RelayConfig; + type OutboundOpenInfo = (); + + #[inline] + fn listen_protocol(&self) -> Self::Protocol { + self.config.clone() + } + + fn inject_fully_negotiated( + &mut self, + protocol: >::Output, + _endpoint: NodeHandlerEndpoint, + ) { + debug_assert!(_endpoint.is_listener()); + + match protocol { + RelayOutput::Sealed(relay) => self.active_relays.push(relay), + RelayOutput::Stream { stream, src_peer_id } => { + let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; + self.queued_events.push(ev); + }, + } + } + + #[inline] + fn inject_event(&mut self, _: &Self::InEvent) {} + + #[inline] + fn inject_inbound_closed(&mut self) {} + + #[inline] + fn inject_dial_upgrade_error(&mut self, _: Self::OutboundOpenInfo, _: io::Error) { + // TODO: report? + } + + #[inline] + fn shutdown(&mut self) { + self.shutdown = true; + } + + fn poll( + &mut self, + ) -> Poll< + Option< + ProtocolsHandlerEvent< + Self::Protocol, + Self::OutboundOpenInfo, + RelayHandlerEvent, + >, + >, + io::Error, + > { + // Report the queued events. + if !self.queued_events.is_empty() { + let event = self.queued_events.remove(0); + return Ok(Async::Ready(Some(ProtocolsHandlerEvent::Custom(event)))); + } + + // We remove each element from `active_relays` one by one and add them back. + for n in (0..self.active_relays.len()).rev() { + let mut relay = self.active_relays.swap_remove(n); + match relay.poll() { + // Don't add back the relay if it's finished or errors. + Ok(Async::Ready(())) | Err(_) => {}, + Ok(Async::NotReady) => self.active_relays.push(relay), + } + } + + Ok(Async::NotReady) + } +} diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index 9a988af0483..581c4cd45bc 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -30,12 +30,15 @@ extern crate rand; extern crate tokio_codec; extern crate tokio_io; extern crate unsigned_varint; +extern crate void; mod copy; +mod handler; mod message; mod protocol; mod transport; mod utility; +pub use handler::{RelayHandler, RelayHandlerEvent}; pub use protocol::{RelayOutput, RelayConfig}; pub use transport::RelayTransport; From ccdf701ad6dacb0d877de497659e927c66f96945 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 15:56:56 +0200 Subject: [PATCH 10/23] Properly shut down --- transports/relay/src/handler.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index b9621bb27d4..0ca9dc113b5 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -155,6 +155,12 @@ where } } + // Shut down process. + if self.shutdown { + self.active_relays.clear(); // TODO: not a proper shutdown + return Ok(Async::Ready(None)); + } + Ok(Async::NotReady) } } From 318369c067476410f52c6ae9e5e24a269e82cbf9 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 17:49:46 +0200 Subject: [PATCH 11/23] Add Send to the boxed futures --- transports/relay/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 0ca9dc113b5..136977810bc 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -37,7 +37,7 @@ pub struct RelayHandler { shutdown: bool, /// List of futures that process relaying data from the remote node to a destination. - active_relays: Vec>>, + active_relays: Vec + Send>>, /// Queue of events to return when polled. queued_events: Vec>, From d77f09249c2ec26b79e14bc31b6c3efa6272bedf Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 24 Oct 2018 21:30:23 +0200 Subject: [PATCH 12/23] Add map_protocol --- core/src/nodes/protocols_handler.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/nodes/protocols_handler.rs b/core/src/nodes/protocols_handler.rs index ff791ffc01f..87937cd4097 100644 --- a/core/src/nodes/protocols_handler.rs +++ b/core/src/nodes/protocols_handler.rs @@ -213,6 +213,26 @@ impl } } + /// If this is `OutboundSubstreamRequest`, maps the protocol to another. + #[inline] + pub fn map_protocol( + self, + map: F, + ) -> ProtocolsHandlerEvent + where + F: FnOnce(TConnectionUpgrade) -> I, + { + match self { + ProtocolsHandlerEvent::OutboundSubstreamRequest { upgrade, info } => { + ProtocolsHandlerEvent::OutboundSubstreamRequest { + upgrade: map(upgrade), + info, + } + } + ProtocolsHandlerEvent::Custom(val) => ProtocolsHandlerEvent::Custom(val), + } + } + /// If this is `Custom`, maps the content to something else. #[inline] pub fn map_custom( From abd11beeb1c3877d86153334c36725700319a92b Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Fri, 26 Oct 2018 11:28:57 +0200 Subject: [PATCH 13/23] Remove Transport from RelayConfig --- transports/relay/src/handler.rs | 29 ++--- transports/relay/src/protocol.rs | 211 +++++++++++++++---------------- transports/relay/src/utility.rs | 1 + 3 files changed, 114 insertions(+), 127 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 136977810bc..c154fe57eb0 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use futures::prelude::*; -use core::{PeerId, Transport}; +use core::PeerId; use core::nodes::handled_node::NodeHandlerEndpoint; use core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; use core::ConnectionUpgrade; @@ -29,9 +29,9 @@ use tokio_io::{AsyncRead, AsyncWrite}; use void::Void; /// Protocol handler that identifies the remote at a regular period. -pub struct RelayHandler { +pub struct RelayHandler { /// Configuration for the protocol. - config: RelayConfig, + config: RelayConfig, /// True if wanting to shut down. shutdown: bool, @@ -56,18 +56,12 @@ pub enum RelayHandlerEvent { }, } -impl RelayHandler -where TTrans: Transport + Clone + 'static, - TTrans::Dial: Send, // TODO: remove - TTrans::Listener: Send, // TODO: remove - TTrans::ListenerUpgrade: Send, // TODO: remove - TTrans::Output: Send + AsyncRead + AsyncWrite, -{ +impl RelayHandler { /// Builds a new `RelayHandler`. #[inline] - pub fn new(my_id: PeerId, transport: TTrans) -> Self { + pub fn new() -> Self { RelayHandler { - config: RelayConfig::new(my_id, transport), + config: RelayConfig::new(), shutdown: false, active_relays: Vec::new(), queued_events: Vec::new(), @@ -75,19 +69,14 @@ where TTrans: Transport + Clone + 'static, } } -impl ProtocolsHandler for RelayHandler +impl ProtocolsHandler for RelayHandler where - TTrans: Transport + Clone + Send + Sync + 'static, - TTrans::Dial: Send, - TTrans::Listener: Send, - TTrans::ListenerUpgrade: Send, - TTrans::Output: AsyncRead + AsyncWrite + Send, TSubstream: AsyncRead + AsyncWrite + Send + Sync + 'static, // TODO: remove useless bounds { type InEvent = Void; type OutEvent = RelayHandlerEvent; type Substream = TSubstream; - type Protocol = RelayConfig; + type Protocol = RelayConfig; type OutboundOpenInfo = (); #[inline] @@ -103,7 +92,7 @@ where debug_assert!(_endpoint.is_listener()); match protocol { - RelayOutput::Sealed(relay) => self.active_relays.push(relay), + RelayOutput::HopRequest(request) => unimplemented!(), RelayOutput::Stream { stream, src_peer_id } => { let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; self.queued_events.push(ev); diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 6e514175e28..b49c0123804 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -20,8 +20,8 @@ use bytes::Bytes; use copy; -use core::{ConnectionUpgrade, Endpoint, PeerId, Transport}; -use futures::{stream, future::{self, Either::{A, B}, FutureResult}, prelude::*}; +use core::{upgrade, ConnectionUpgrade, Endpoint, PeerId}; +use futures::{future::{self, Either::{A, B}, FutureResult}, prelude::*}; use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; use std::{io, iter}; use tokio_io::{AsyncRead, AsyncWrite}; @@ -29,14 +29,7 @@ use utility::{io_err, is_success, status, Io, Peer}; /// Configuration for a connection upgrade that handles the relay protocol. #[derive(Debug, Clone)] -pub struct RelayConfig { - /// Peer id of the local node. - my_id: PeerId, - /// When asked to relay a connection, the transport to use to reach the requested node. - transport: T, - /// If `allow_relays` is false this node can only be used as a destination but will not allow - /// relaying streams to other destinations. - allow_relays: bool +pub struct RelayConfig { } /// The `RelayConfig` upgrade can serve as destination or relay. Each mode needs a different @@ -55,20 +48,89 @@ pub enum RelayOutput { // TODO: also provide the addresses ; however the semantics of these addresses is uncertain }, - /// We have been asked to relay communications to another node. Polling the future until it's - /// ready will process the proxying. - // TODO: provide more info for informative purposes for the user - Sealed(Box + Send>), + /// We have been asked to relay communications to another node. + HopRequest(RelayHopRequest), } -impl ConnectionUpgrade for RelayConfig +/// Request from a remote for us to relay communications to another node. +pub struct RelayHopRequest { + /// The stream to the source. + stream: Io, + /// Target of the request. + dest: Peer, +} + +impl RelayHopRequest +where TStream: AsyncRead + AsyncWrite + 'static +{ + /// Accepts the request by providing a stream to the destination. + /// + /// The `dest_stream` should be a brand new dialing substream. This method will negotiate the + /// `relay` protocol on it, send a relay message, and then relay to it the connection from the + /// source. + /// + /// > **Note**: It is important that you process the `Future` that this method returns, + /// > otherwise the relaying will not work. + pub fn fulfill(self, dest_stream: TDestStream) -> impl Future + where TDestStream: AsyncRead + AsyncWrite + 'static + { + let source_stream = self.stream; + let stop = stop_message(&Peer::from_message(CircuitRelay_Peer::new()).unwrap(), &self.dest); + upgrade::apply(dest_stream, TrivialUpgrade, Endpoint::Dialer) + .and_then(|dest_stream| { + // send STOP message to destination and expect back a SUCCESS message + Io::new(dest_stream).send(stop) + .and_then(Io::recv) + .and_then(|(response, io)| { + let rsp = match response { + Some(m) => m, + None => return Err(io_err("no message from destination")) + }; + + if is_success(&rsp) { + Ok(io.into()) + } else { + Err(io_err("no success response from relay")) + } + }) + }) + // signal success or failure to source + .then(move |result| { + match result { + Ok(c) => { + let msg = status(CircuitRelay_Status::SUCCESS); + A(source_stream.send(msg).map(|io| (io.into(), c))) + } + Err(e) => { + let msg = status(CircuitRelay_Status::HOP_CANT_DIAL_DST); + B(source_stream.send(msg).and_then(|_| Err(e))) + } + } + }) + // return future for bidirectional data transfer + .and_then(move |(src, dst)| { + let (src_r, src_w) = src.split(); + let (dst_r, dst_w) = dst.split(); + let a = copy::flushing_copy(src_r, dst_w).map(|_| ()); + let b = copy::flushing_copy(dst_r, src_w).map(|_| ()); + a.select(b).map(|_| ()).map_err(|(e, _)| e) + }) + } + + /// Refuses the request. + /// + /// The returned `Future` gracefully shuts down the request. + #[inline] + pub fn deny(self) -> impl Future { + // TODO: correct status + let message = hop_deny_message(CircuitRelay_Status::HOP_CANT_RELAY_TO_SELF); + self.stream.send(message).map(|_| ()) + } +} + +impl ConnectionUpgrade for RelayConfig where C: AsyncRead + AsyncWrite + Send + 'static, - T: Transport + Clone + Send + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Output: AsyncRead + AsyncWrite + Send, { type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>; type UpgradeIdentifier = (); @@ -88,8 +150,8 @@ where return A(A(future::err(io_err("no message received")))) }; match msg.get_field_type() { - CircuitRelay_Type::HOP if self.allow_relays => { // act as relay - B(A(self.on_hop(msg, io).map(|fut| RelayOutput::Sealed(Box::new(fut))))) + CircuitRelay_Type::HOP => { // act as relay + B(A(self.on_hop(msg, io))) } CircuitRelay_Type::STOP => { // act as destination B(B(self.on_stop(msg, io))) @@ -105,26 +167,14 @@ where } } -impl RelayConfig -where - T: Transport + Clone + 'static, - T::Dial: Send, // TODO: remove - T::Listener: Send, // TODO: remove - T::ListenerUpgrade: Send, // TODO: remove - T::Output: Send + AsyncRead + AsyncWrite, -{ +impl RelayConfig { /// Builds a new `RelayConfig` with default options. - pub fn new(my_id: PeerId, transport: T) -> RelayConfig { - RelayConfig { my_id, transport, allow_relays: true } - } - - /// Sets whether we will allow requests for relaying connections. - pub fn allow_relays(&mut self, val: bool) { - self.allow_relays = val + pub fn new() -> RelayConfig { + RelayConfig {} } /// HOP message handling (request to act as a relay). - fn on_hop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error=io::Error> + fn on_hop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error=io::Error> where C: AsyncRead + AsyncWrite + 'static, { @@ -132,76 +182,20 @@ where peer } else { let msg = status(CircuitRelay_Status::HOP_SRC_MULTIADDR_INVALID); - return A(io.send(msg).and_then(|_| Err(io_err("invalid src address")))) + return B(A(io.send(msg).and_then(|_| Err(io_err("invalid src address"))))) }; let dest = if let Some(peer) = Peer::from_message(msg.take_dstPeer()) { peer } else { let msg = status(CircuitRelay_Status::HOP_DST_MULTIADDR_INVALID); - return B(A(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) + return B(B(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) }; - let stop = stop_message(&from, &dest); - - let transport = self.transport.with_upgrade(TrivialUpgrade); - let dest_id = dest.id; - let future = stream::iter_ok(dest.addrs.into_iter()) - .and_then(move |dest_addr| { - transport.clone().dial(dest_addr).map_err(|_| io_err("failed to dial")) - }) - .and_then(|dial| dial) - .then(|result| Ok(result.ok())) - .filter_map(|result| result) - .into_future() - .map_err(|(err, _stream)| err) - .and_then(move |(ok, _stream)| { - if let Some(c) = ok { - // send STOP message to destination and expect back a SUCCESS message - let future = Io::new(c).send(stop) - .and_then(Io::recv) - .and_then(|(response, io)| { - let rsp = match response { - Some(m) => m, - None => return Err(io_err("no message from destination")) - }; - if is_success(&rsp) { - Ok(io.into()) - } else { - Err(io_err("no success response from relay")) - } - }); - A(future) - } else { - B(future::err(io_err(format!("could not dial to {:?}", dest_id)))) - } - }) - // signal success or failure to source - .then(move |result| { - match result { - Ok(c) => { - let msg = status(CircuitRelay_Status::SUCCESS); - A(io.send(msg).map(|io| (io.into(), c))) - } - Err(e) => { - let msg = status(CircuitRelay_Status::HOP_CANT_DIAL_DST); - B(io.send(msg).and_then(|_| Err(e))) - } - } - }) - // return future for bidirectional data transfer - .and_then(move |(src, dst)| { - let future = { - let (src_r, src_w) = src.split(); - let (dst_r, dst_w) = dst.split(); - let a = copy::flushing_copy(src_r, dst_w).map(|_| ()); - let b = copy::flushing_copy(dst_r, src_w).map(|_| ()); - a.select(b).map(|_| ()).map_err(|(e, _)| e) - }; - Ok(future) - }); - - B(B(future)) + A(future::ok(RelayOutput::HopRequest(RelayHopRequest { + stream: io, + dest, + }))) } /// STOP message handling (we are a destination) @@ -223,17 +217,12 @@ where return A(B(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) }; - if dest.id != self.my_id { - let msg = status(CircuitRelay_Status::STOP_RELAY_REFUSED); - return B(A(io.send(msg).and_then(|_| Err(io_err("destination id mismatch"))))) - } - - B(B(io.send(status(CircuitRelay_Status::SUCCESS)).map(move |stream| { + B(io.send(status(CircuitRelay_Status::SUCCESS)).map(move |stream| { RelayOutput::Stream { stream: stream.into(), src_peer_id: from.id, } - }))) + })) } } @@ -258,6 +247,14 @@ fn stop_message(from: &Peer, dest: &Peer) -> CircuitRelay { msg } +/// Builds a message that refuses a HOP request. +fn hop_deny_message(status: CircuitRelay_Status) -> CircuitRelay { + let mut msg = CircuitRelay::new(); + msg.set_field_type(CircuitRelay_Type::STATUS); // TODO: is this correct? + msg.set_code(status); + msg +} + /// Dummy connection upgrade that negotiates the relay protocol and returns the socket. #[derive(Debug, Clone)] struct TrivialUpgrade; diff --git a/transports/relay/src/utility.rs b/transports/relay/src/utility.rs index a6805e49195..b001a68a90c 100644 --- a/transports/relay/src/utility.rs +++ b/transports/relay/src/utility.rs @@ -51,6 +51,7 @@ impl Io { } } + // TODO: does this work for sure? isn't there some issue with an out cache being dropped silently? pub(crate) fn into(self) -> T { self.codec.into_inner() } From 11cbc399d0df2fceb6afcca7ff775d8f23b6698d Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sun, 28 Oct 2018 18:03:17 +0100 Subject: [PATCH 14/23] Remove RelayTransport --- transports/relay/src/lib.rs | 2 - transports/relay/src/transport.rs | 152 ------------------------------ 2 files changed, 154 deletions(-) delete mode 100644 transports/relay/src/transport.rs diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index 581c4cd45bc..1b466e74c68 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -36,9 +36,7 @@ mod copy; mod handler; mod message; mod protocol; -mod transport; mod utility; pub use handler::{RelayHandler, RelayHandlerEvent}; pub use protocol::{RelayOutput, RelayConfig}; -pub use transport::RelayTransport; diff --git a/transports/relay/src/transport.rs b/transports/relay/src/transport.rs deleted file mode 100644 index 6d9882c1db4..00000000000 --- a/transports/relay/src/transport.rs +++ /dev/null @@ -1,152 +0,0 @@ -// 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 core::{PeerId, Transport}; -use futures::{stream, prelude::*}; -use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Type}; -use multiaddr::Multiaddr; -use protocol; -use std::io; -use tokio_io::{AsyncRead, AsyncWrite}; -use utility::{io_err, Peer, RelayAddr}; - -/// Implementation of `Transport` that supports addresses of the form -/// `/p2p-circuit/`. -/// -/// `` is the address of a node that should act as a relay, and `` is the address -/// of the destination, that the relay should dial. -#[derive(Debug, Clone)] -pub struct RelayTransport { - /// Id of the local node. - my_id: PeerId, - /// Transport to use to. - transport: T, -} - -impl Transport for RelayTransport -where - T: Transport + Send + Clone + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Output: AsyncRead + AsyncWrite + Send, -{ - type Output = T::Output; - type Listener = Box + Send>; - type ListenerUpgrade = Box + Send>; - type Dial = Box + Send>; - - fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { - Err((self, addr)) - } - - fn dial(self, addr: Multiaddr) -> Result { - match RelayAddr::parse(&addr) { - RelayAddr::Malformed => { - debug!("malformed address: {}", addr); - return Err((self, addr)); - } - RelayAddr::Multihop => { - debug!("multihop address: {}", addr); - return Err((self, addr)); - } - RelayAddr::Address { relay, dest } => { - let f = self.relay_via(&relay, &dest).map_err(|this| (this, addr))?; - Ok(Box::new(f)) - } - } - } - - fn nat_traversal(&self, a: &Multiaddr, b: &Multiaddr) -> Option { - self.transport.nat_traversal(a, b) - } -} - -impl RelayTransport -where - T: Transport + Clone + 'static, - T::Dial: Send, - T::Listener: Send, - T::ListenerUpgrade: Send, - T::Output: AsyncRead + AsyncWrite + Send, -{ - /// Create a new relay transport. - /// - /// This transport uses a static set of relays and will not attempt - /// any relay discovery. - pub fn new(my_id: PeerId, transport: T) -> Self { - RelayTransport { - my_id, - transport, - } - } - - // Relay to destination via the given peer. - fn relay_via(self, relay: &Peer, destination: &Peer) -> Result, Self> { - trace!("relay_via {:?} to {:?}", relay.id, destination.id); - - // no relay address => bail out - if relay.addrs.is_empty() { - info!("no available address for relay: {:?}", relay.id); - return Err(self); - } - - let relay = relay.clone(); - let message = self.hop_message(destination); - let transport = self.transport.with_upgrade(protocol::Source(message)); - let future = stream::iter_ok(relay.addrs.clone().into_iter()) - .filter_map(move |addr| transport.clone().dial(addr).ok()) - .and_then(|dial| dial) - .then(|result| Ok(result.ok())) - .filter_map(|result| result) - .into_future() - .map_err(|(err, _stream)| err) - .and_then(move |(ok, _stream)| match ok { - Some(out) => { - debug!("connected"); - Ok(out) - } - None => { - info!("failed to dial to {:?}", relay.id); - Err(io_err(format!("failed to dial to relay {:?}", relay.id))) - } - }); - Ok(future) - } - - fn hop_message(&self, destination: &Peer) -> CircuitRelay { - let mut msg = CircuitRelay::new(); - msg.set_field_type(CircuitRelay_Type::HOP); - - let mut from = CircuitRelay_Peer::new(); - from.set_id(self.my_id.as_bytes().to_vec()); - // TODO: fill from.mut_addrs() - msg.set_srcPeer(from); - - let mut dest = CircuitRelay_Peer::new(); - dest.set_id(destination.id.as_bytes().to_vec()); - for a in &destination.addrs { - dest.mut_addrs().push(a.to_bytes()) - } - msg.set_dstPeer(dest); - - msg - } -} From ef9200cbf7557dd4cf8306b60ef01dce59949d5f Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sun, 28 Oct 2018 18:37:37 +0100 Subject: [PATCH 15/23] Add a destination request struct --- transports/relay/src/handler.rs | 7 +-- transports/relay/src/protocol.rs | 86 ++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index c154fe57eb0..0cc01218501 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -93,9 +93,10 @@ where match protocol { RelayOutput::HopRequest(request) => unimplemented!(), - RelayOutput::Stream { stream, src_peer_id } => { - let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; - self.queued_events.push(ev); + RelayOutput::DestinationRequest(destination) => { + unimplemented!() + /*let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; + self.queued_events.push(ev);*/ }, } } diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index b49c0123804..679e3b46694 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -38,21 +38,64 @@ pub struct RelayConfig { /// Therefore, in the latter case we simply return a future that can be driven to completion /// but otherwise the stream is not programmatically accessible. pub enum RelayOutput { - /// The source is a relay `R` that relays the communication from someone `S` to us. Keep in - /// mind that the multiaddress you have about this communication is the address of `R`. - Stream { - /// Stream of data to the original source. - stream: TStream, - /// Identifier of the original source `S`. - src_peer_id: PeerId, - // TODO: also provide the addresses ; however the semantics of these addresses is uncertain - }, - + /// We have been asked to become a destination. + DestinationRequest(RelayDestinationRequest), /// We have been asked to relay communications to another node. HopRequest(RelayHopRequest), } +/// Request from a remote for us to become a destination. +#[must_use = "A destination request should be either accepted or denied"] +pub struct RelayDestinationRequest { + /// The stream to the source. + stream: Io, + /// Source of the request. + from: Peer, +} + +impl RelayDestinationRequest +where TStream: AsyncRead + AsyncWrite + 'static +{ + /// Peer id of the source that is being relayed. + pub fn source_id(&self) -> &PeerId { + &self.from.id + } + + // TODO: addresses + + /// Accepts the request. + pub fn accept(self) -> impl Future { + // send a SUCCESS message + self.stream + .send(status(CircuitRelay_Status::SUCCESS)) + .and_then(Io::recv) + .and_then(|(response, io)| { + let rsp = match response { + Some(m) => m, + None => return Err(io_err("no message from destination")) + }; + + if is_success(&rsp) { + Ok(io.into()) + } else { + Err(io_err("no success response from relay")) + } + }) + } + + /// Refuses the request. + /// + /// The returned `Future` gracefully shuts down the request. + #[inline] + pub fn deny(self) -> impl Future { + // TODO: correct status + let message = deny_message(CircuitRelay_Status::STOP_RELAY_REFUSED); + self.stream.send(message).map(|_| ()) + } +} + /// Request from a remote for us to relay communications to another node. +#[must_use = "A HOP request should be either accepted or denied"] pub struct RelayHopRequest { /// The stream to the source. stream: Io, @@ -123,7 +166,7 @@ where TStream: AsyncRead + AsyncWrite + 'static #[inline] pub fn deny(self) -> impl Future { // TODO: correct status - let message = hop_deny_message(CircuitRelay_Status::HOP_CANT_RELAY_TO_SELF); + let message = deny_message(CircuitRelay_Status::HOP_CANT_RELAY_TO_SELF); self.stream.send(message).map(|_| ()) } } @@ -207,22 +250,13 @@ impl RelayConfig { peer } else { let msg = status(CircuitRelay_Status::HOP_SRC_MULTIADDR_INVALID); - return A(A(io.send(msg).and_then(|_| Err(io_err("invalid src address"))))) - }; - - let dest = if let Some(peer) = Peer::from_message(msg.take_dstPeer()) { - peer - } else { - let msg = status(CircuitRelay_Status::STOP_DST_MULTIADDR_INVALID); - return A(B(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) + return B(io.send(msg).and_then(|_| Err(io_err("invalid src address")))); }; - B(io.send(status(CircuitRelay_Status::SUCCESS)).map(move |stream| { - RelayOutput::Stream { - stream: stream.into(), - src_peer_id: from.id, - } - })) + A(future::ok(RelayOutput::DestinationRequest(RelayDestinationRequest { + stream: io, + from, + }))) } } @@ -248,7 +282,7 @@ fn stop_message(from: &Peer, dest: &Peer) -> CircuitRelay { } /// Builds a message that refuses a HOP request. -fn hop_deny_message(status: CircuitRelay_Status) -> CircuitRelay { +fn deny_message(status: CircuitRelay_Status) -> CircuitRelay { let mut msg = CircuitRelay::new(); msg.set_field_type(CircuitRelay_Type::STATUS); // TODO: is this correct? msg.set_code(status); From 2f3b4a7ef15b6fad86fc435f3ddaaac00ea39349 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sun, 28 Oct 2018 19:03:20 +0100 Subject: [PATCH 16/23] Add WaitRequest --- transports/relay/src/handler.rs | 1 + transports/relay/src/protocol.rs | 109 ++++++++++++++++++++++++------- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 0cc01218501..9a506156222 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -98,6 +98,7 @@ where /*let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; self.queued_events.push(ev);*/ }, + RelayOutput::WaitRequest(_) => unimplemented!(), } } diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 679e3b46694..311279bdc7f 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -20,7 +20,7 @@ use bytes::Bytes; use copy; -use core::{upgrade, ConnectionUpgrade, Endpoint, PeerId}; +use core::{upgrade, ConnectionUpgrade, Endpoint, Multiaddr, PeerId}; use futures::{future::{self, Either::{A, B}, FutureResult}, prelude::*}; use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; use std::{io, iter}; @@ -38,12 +38,52 @@ pub struct RelayConfig { /// Therefore, in the latter case we simply return a future that can be driven to completion /// but otherwise the stream is not programmatically accessible. pub enum RelayOutput { + /// We are successfully connected as a dialer, and we can now request the remote to act as a + /// proxy. + WaitRequest(RelayWaitRequest), /// We have been asked to become a destination. DestinationRequest(RelayDestinationRequest), /// We have been asked to relay communications to another node. HopRequest(RelayHopRequest), } +/// We are successfully connected as a dialer, and we can now request the remote to act as a proxy. +#[must_use = "There is no point in opening a request if you don't use"] +pub struct RelayWaitRequest { + /// The stream of the destination. + stream: Io, +} + +impl RelayWaitRequest +where TStream: AsyncRead + AsyncWrite + 'static +{ + /// Request proxying to a destination. + pub fn request(self, dest_id: PeerId, dest_addresses: impl IntoIterator) + -> impl Future + { + let msg = hop_message(&Peer { + id: dest_id, + addrs: dest_addresses.into_iter().collect(), + }); + + self.stream + .send(msg) + .and_then(Io::recv) + .and_then(|(response, io)| { + let rsp = match response { + Some(m) => m, + None => return Err(io_err("no message from relay")), + }; + + if is_success(&rsp) { + Ok(io.into()) + } else { + Err(io_err("no success response from relay")) + } + }) + } +} + /// Request from a remote for us to become a destination. #[must_use = "A destination request should be either accepted or denied"] pub struct RelayDestinationRequest { @@ -185,28 +225,35 @@ where type Output = RelayOutput; type Future = Box + Send>; - fn upgrade(self, conn: C, _: (), _: Endpoint) -> Self::Future { - let future = Io::new(conn).recv().and_then(move |(message, io)| { - let msg = if let Some(m) = message { - m - } else { - return A(A(future::err(io_err("no message received")))) - }; - match msg.get_field_type() { - CircuitRelay_Type::HOP => { // act as relay - B(A(self.on_hop(msg, io))) - } - CircuitRelay_Type::STOP => { // act as destination - B(B(self.on_stop(msg, io))) - } - other => { - debug!("invalid message type: {:?}", other); - let resp = status(CircuitRelay_Status::MALFORMED_MESSAGE); - A(B(io.send(resp).and_then(|_| Err(io_err("invalid message type"))))) + fn upgrade(self, conn: C, _: (), endpoint: Endpoint) -> Self::Future { + if let Endpoint::Dialer = endpoint { + let future = future::ok(RelayOutput::WaitRequest(RelayWaitRequest { + stream: Io::new(conn), + })); + Box::new(future) + } else { + let future = Io::new(conn).recv().and_then(move |(message, io)| { + let msg = if let Some(m) = message { + m + } else { + return A(A(future::err(io_err("no message received")))) + }; + match msg.get_field_type() { + CircuitRelay_Type::HOP => { // act as relay + B(A(self.on_hop(msg, io))) + } + CircuitRelay_Type::STOP => { // act as destination + B(B(self.on_stop(msg, io))) + } + other => { + debug!("invalid message type: {:?}", other); + let resp = status(CircuitRelay_Status::MALFORMED_MESSAGE); + A(B(io.send(resp).and_then(|_| Err(io_err("invalid message type"))))) + } } - } - }); - Box::new(future) + }); + Box::new(future) + } } } @@ -241,7 +288,7 @@ impl RelayConfig { }))) } - /// STOP message handling (we are a destination) + /// STOP message handling (we are a destination). fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error = io::Error> where C: AsyncRead + AsyncWrite + 'static, @@ -260,6 +307,22 @@ impl RelayConfig { } } +/// Generates a message that requests proxying. +fn hop_message(destination: &Peer) -> CircuitRelay { + let mut msg = CircuitRelay::new(); + msg.set_field_type(CircuitRelay_Type::HOP); + + let mut dest = CircuitRelay_Peer::new(); + dest.set_id(destination.id.as_bytes().to_vec()); + for a in &destination.addrs { + dest.mut_addrs().push(a.to_bytes()) + } + msg.set_dstPeer(dest); + + msg +} + +/// Generates a STOP message to send to a destination. fn stop_message(from: &Peer, dest: &Peer) -> CircuitRelay { let mut msg = CircuitRelay::new(); msg.set_field_type(CircuitRelay_Type::STOP); From 959912d616136fdc71c0c7921f10a25a7b66b273 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sun, 28 Oct 2018 19:03:50 +0100 Subject: [PATCH 17/23] Rename WaitRequest to ProxyRequest --- transports/relay/src/handler.rs | 2 +- transports/relay/src/protocol.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 9a506156222..d199491c41c 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -98,7 +98,7 @@ where /*let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; self.queued_events.push(ev);*/ }, - RelayOutput::WaitRequest(_) => unimplemented!(), + RelayOutput::ProxyRequest(_) => unimplemented!(), } } diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 311279bdc7f..7ac9a6d5472 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -37,10 +37,11 @@ pub struct RelayConfig { /// we pipe data from source to destination and do not want to use the stream in any other way. /// Therefore, in the latter case we simply return a future that can be driven to completion /// but otherwise the stream is not programmatically accessible. +// TODO: debug pub enum RelayOutput { /// We are successfully connected as a dialer, and we can now request the remote to act as a /// proxy. - WaitRequest(RelayWaitRequest), + ProxyRequest(RelayProxyRequest), /// We have been asked to become a destination. DestinationRequest(RelayDestinationRequest), /// We have been asked to relay communications to another node. @@ -48,13 +49,14 @@ pub enum RelayOutput { } /// We are successfully connected as a dialer, and we can now request the remote to act as a proxy. +// TODO: debug #[must_use = "There is no point in opening a request if you don't use"] -pub struct RelayWaitRequest { +pub struct RelayProxyRequest { /// The stream of the destination. stream: Io, } -impl RelayWaitRequest +impl RelayProxyRequest where TStream: AsyncRead + AsyncWrite + 'static { /// Request proxying to a destination. @@ -85,6 +87,7 @@ where TStream: AsyncRead + AsyncWrite + 'static } /// Request from a remote for us to become a destination. +// TODO: debug #[must_use = "A destination request should be either accepted or denied"] pub struct RelayDestinationRequest { /// The stream to the source. @@ -135,6 +138,7 @@ where TStream: AsyncRead + AsyncWrite + 'static } /// Request from a remote for us to relay communications to another node. +// TODO: debug #[must_use = "A HOP request should be either accepted or denied"] pub struct RelayHopRequest { /// The stream to the source. @@ -227,7 +231,7 @@ where fn upgrade(self, conn: C, _: (), endpoint: Endpoint) -> Self::Future { if let Endpoint::Dialer = endpoint { - let future = future::ok(RelayOutput::WaitRequest(RelayWaitRequest { + let future = future::ok(RelayOutput::ProxyRequest(RelayProxyRequest { stream: Io::new(conn), })); Box::new(future) From 9fbbefb9d24a73e5d4f4843f6b3ec2fcdcf98af0 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 30 Oct 2018 08:22:41 +0100 Subject: [PATCH 18/23] Update the handler as well --- transports/relay/src/handler.rs | 192 +++++++++++++++++++++++++------ transports/relay/src/lib.rs | 2 +- transports/relay/src/protocol.rs | 8 ++ 3 files changed, 163 insertions(+), 39 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index d199491c41c..8a292dd24d5 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -19,65 +19,139 @@ // DEALINGS IN THE SOFTWARE. use futures::prelude::*; -use core::PeerId; use core::nodes::handled_node::NodeHandlerEndpoint; use core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; -use core::ConnectionUpgrade; -use protocol::{RelayOutput, RelayConfig}; -use std::io; +use core::{ConnectionUpgrade, Multiaddr, PeerId}; +use protocol::{RelayDestinationRequest, RelayHopRequest, RelayOutput, RelayConfig}; +use std::{cell::RefCell, io, marker::PhantomData}; use tokio_io::{AsyncRead, AsyncWrite}; -use void::Void; /// Protocol handler that identifies the remote at a regular period. -pub struct RelayHandler { +pub struct RelayHandler { /// Configuration for the protocol. config: RelayConfig, /// True if wanting to shut down. shutdown: bool, - /// List of futures that process relaying data from the remote node to a destination. - active_relays: Vec + Send>>, + /// List of futures that must be processed. Contains notably the futures that relay data to + /// a destination, or the futures that send a negative response back. + active_futures: Vec + Send>>, + + /// List of peers we should ask the remote to relay to. + relay_requests: Vec<(PeerId, Vec)>, /// Queue of events to return when polled. queued_events: Vec>, + + /// Phantom data. + marker: PhantomData } -/// Event produced by the periodic identifier. -#[derive(Debug)] +/// Event produced by the relay handler. +//#[derive(Debug)] // TODO: restore pub enum RelayHandlerEvent { + /// The remote wants us to relay communications to a third party. + HopRequest(RelayHandlerHopRequest), /// The remote is a relay and is relaying a connection to us. In other words, we are used as /// destination. - Destination { - /// I/O to the source. - stream: TSubstream, - /// Peer id of the source. - src_peer_id: PeerId, + DestinationRequest(RelayHandlerDestRequest), +} + +/// Event produced by the relay handler. +//#[derive(Debug)] // TODO: restore +pub enum RelayHandlerIn { + /// Accept a hop request from the remote. + AcceptHopRequest { + /// The request that was produced by the handler earlier. + request: RefCell>>, + /// The substream to the destination. + dest_substream: RefCell>, + }, + /// Denies a hop request from the remote. + DenyHopRequest(RefCell>>), + /// Opens a new substream to the remote and asks it to relay communications to a third party. + RelayRequest { + /// Id of the peer to connect to. + target: PeerId, + /// Addresses known for this peer. + addresses: Vec, }, } -impl RelayHandler { +/// The remote wants us to be treated as a destination. +pub struct RelayHandlerHopRequest { + inner: RelayHopRequest, +} + +impl RelayHandlerHopRequest +where TSubstream: AsyncRead + AsyncWrite + 'static +{ + /// Peer id of the target that we should relay to. + #[inline] + pub fn target_id(&self) -> &PeerId { + self.inner.target_id() + } + + // TODO: addresses +} + +/// The remote wants us to be treated as a destination. +pub struct RelayHandlerDestRequest { + inner: RelayDestinationRequest, +} + +impl RelayHandlerDestRequest +where TSubstream: AsyncRead + AsyncWrite + 'static +{ + /// Peer id of the source that is being relayed. + #[inline] + pub fn source_id(&self) -> &PeerId { + self.inner.source_id() + } + + // TODO: addresses + + /// Accepts the request. Produces a `Future` that sends back a success message then provides + /// the stream to the source. + #[inline] + pub fn accept(self) -> impl Future { + self.inner.accept() + } + + /// Denies the request. Produces a `Future` that sends back an error message to the proxyy. + #[inline] + pub fn deny(self) -> impl Future { + self.inner.deny() + } +} + +impl RelayHandler { /// Builds a new `RelayHandler`. #[inline] pub fn new() -> Self { RelayHandler { config: RelayConfig::new(), shutdown: false, - active_relays: Vec::new(), + active_futures: Vec::new(), + relay_requests: Vec::new(), queued_events: Vec::new(), + marker: PhantomData, } } } -impl ProtocolsHandler for RelayHandler +impl ProtocolsHandler for RelayHandler where TSubstream: AsyncRead + AsyncWrite + Send + Sync + 'static, // TODO: remove useless bounds + TDestSubstream: AsyncRead + AsyncWrite + Send + Sync + 'static, // TODO: remove useless bounds { - type InEvent = Void; + type InEvent = RelayHandlerIn; type OutEvent = RelayHandlerEvent; type Substream = TSubstream; type Protocol = RelayConfig; - type OutboundOpenInfo = (); + // The information is the peer we want to relay communications to. + type OutboundOpenInfo = (PeerId, Vec); #[inline] fn listen_protocol(&self) -> Self::Protocol { @@ -87,23 +161,56 @@ where fn inject_fully_negotiated( &mut self, protocol: >::Output, - _endpoint: NodeHandlerEndpoint, + endpoint: NodeHandlerEndpoint, ) { - debug_assert!(_endpoint.is_listener()); - - match protocol { - RelayOutput::HopRequest(request) => unimplemented!(), - RelayOutput::DestinationRequest(destination) => { - unimplemented!() - /*let ev = RelayHandlerEvent::Destination { stream, src_peer_id }; - self.queued_events.push(ev);*/ + match (protocol, endpoint) { + (RelayOutput::HopRequest(inner), _) => { + // The remote wants us to relay communications to a third party. + let ev = RelayHandlerEvent::HopRequest(RelayHandlerHopRequest { + inner, + }); + self.queued_events.push(ev); + }, + (RelayOutput::DestinationRequest(inner), _) => { + // The remote wants to use us as a destination. + let ev = RelayHandlerEvent::DestinationRequest(RelayHandlerDestRequest { + inner, + }); + self.queued_events.push(ev); + }, + (RelayOutput::ProxyRequest(request), NodeHandlerEndpoint::Dialer((peer_id, addresses))) => { + // We can ask the remote for what we want it to do. + request.request(peer_id, addresses); + }, + (RelayOutput::ProxyRequest(_), NodeHandlerEndpoint::Listener) => { + // This shouldn't happen. + debug_assert!(false, "the protocol forbids ProxyRequest when listening"); }, - RelayOutput::ProxyRequest(_) => unimplemented!(), } } #[inline] - fn inject_event(&mut self, _: &Self::InEvent) {} + fn inject_event(&mut self, event: &Self::InEvent) { + match event { + RelayHandlerIn::AcceptHopRequest { request, dest_substream } => { + if let Some(request) = request.borrow_mut().take() { + if let Some(dest_substream) = dest_substream.borrow_mut().take() { + let fut = request.inner.fulfill(dest_substream); + self.active_futures.push(Box::new(fut)); + } + } + }, + RelayHandlerIn::DenyHopRequest(rq) => { + if let Some(rq) = rq.borrow_mut().take() { + let fut = rq.inner.deny(); + self.active_futures.push(Box::new(fut)); + } + }, + RelayHandlerIn::RelayRequest { target, addresses } => { + self.relay_requests.push((target.clone(), addresses.clone())); + }, + } + } #[inline] fn inject_inbound_closed(&mut self) {} @@ -130,25 +237,34 @@ where >, io::Error, > { + // Open substreams if necessary. + if !self.relay_requests.is_empty() { + let rq = self.relay_requests.remove(0); + return Ok(Async::Ready(Some(ProtocolsHandlerEvent::OutboundSubstreamRequest { + info: rq, + upgrade: self.config.clone(), + }))); + } + // Report the queued events. if !self.queued_events.is_empty() { let event = self.queued_events.remove(0); return Ok(Async::Ready(Some(ProtocolsHandlerEvent::Custom(event)))); } - // We remove each element from `active_relays` one by one and add them back. - for n in (0..self.active_relays.len()).rev() { - let mut relay = self.active_relays.swap_remove(n); - match relay.poll() { - // Don't add back the relay if it's finished or errors. + // We remove each element from `active_futures` one by one and add them back. + for n in (0..self.active_futures.len()).rev() { + let mut future = self.active_futures.swap_remove(n); + match future.poll() { + // Don't add back the future if it's finished or errors. Ok(Async::Ready(())) | Err(_) => {}, - Ok(Async::NotReady) => self.active_relays.push(relay), + Ok(Async::NotReady) => self.active_futures.push(future), } } // Shut down process. if self.shutdown { - self.active_relays.clear(); // TODO: not a proper shutdown + self.active_futures.clear(); // TODO: not a proper shutdown return Ok(Async::Ready(None)); } diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index 1b466e74c68..4591896999d 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -38,5 +38,5 @@ mod message; mod protocol; mod utility; -pub use handler::{RelayHandler, RelayHandlerEvent}; +pub use handler::{RelayHandler, RelayHandlerEvent, RelayHandlerHopRequest, RelayHandlerDestRequest}; pub use protocol::{RelayOutput, RelayConfig}; diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs index 7ac9a6d5472..b99db9aff35 100644 --- a/transports/relay/src/protocol.rs +++ b/transports/relay/src/protocol.rs @@ -150,6 +150,14 @@ pub struct RelayHopRequest { impl RelayHopRequest where TStream: AsyncRead + AsyncWrite + 'static { + /// Peer id of the node we should relay communications to. + #[inline] + pub fn target_id(&self) -> &PeerId { + &self.dest.id + } + + // TODO: addresses + /// Accepts the request by providing a stream to the destination. /// /// The `dest_stream` should be a brand new dialing substream. This method will negotiate the From d4f1a1ca9fadf30d89487b3d35728e6c98536416 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 28 Nov 2018 19:46:35 +0100 Subject: [PATCH 19/23] Make protocol compile --- transports/relay/src/codec.rs | 73 +++ transports/relay/src/handler.rs | 6 +- transports/relay/src/lib.rs | 8 +- transports/relay/src/protocol.rs | 433 ------------------ transports/relay/src/protocol/dest_request.rs | 151 ++++++ transports/relay/src/protocol/hop_request.rs | 200 ++++++++ transports/relay/src/protocol/listen.rs | 154 +++++++ transports/relay/src/protocol/mod.rs | 25 + .../relay/src/protocol/relay_request.rs | 140 ++++++ transports/relay/src/protocol/target_open.rs | 88 ++++ transports/relay/src/utility.rs | 1 + 11 files changed, 839 insertions(+), 440 deletions(-) create mode 100644 transports/relay/src/codec.rs delete mode 100644 transports/relay/src/protocol.rs create mode 100644 transports/relay/src/protocol/dest_request.rs create mode 100644 transports/relay/src/protocol/hop_request.rs create mode 100644 transports/relay/src/protocol/listen.rs create mode 100644 transports/relay/src/protocol/mod.rs create mode 100644 transports/relay/src/protocol/relay_request.rs create mode 100644 transports/relay/src/protocol/target_open.rs diff --git a/transports/relay/src/codec.rs b/transports/relay/src/codec.rs new file mode 100644 index 00000000000..53514437f56 --- /dev/null +++ b/transports/relay/src/codec.rs @@ -0,0 +1,73 @@ +// 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 crate::message::CircuitRelay; +use bytes::BytesMut; +use protobuf::{self, Message}; +use std::{fmt, error::Error}; +use tokio_codec::{Encoder, Decoder}; +use unsigned_varint::codec::UviBytes; + +/// Encoder and decoder for protocol messages. +//#[derive(Debug)] // TODO: +pub struct Codec { + inner: UviBytes>, +} + +impl Codec { + /// Creates a `Codec`. + #[inline] + pub fn new() -> Self { + Codec { + inner: UviBytes::default(), + } + } +} + +impl fmt::Debug for Codec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: + write!(f, "Codec") + } +} + +impl Encoder for Codec { + type Item = CircuitRelay; + type Error = Box; + + fn encode(&mut self, msg: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + let pkg = msg.write_to_bytes()?; + self.inner.encode(pkg, dst)?; + Ok(()) + } +} + +impl Decoder for Codec { + type Item = CircuitRelay; + type Error = Box; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + if let Some(p) = self.inner.decode(src)? { + Ok(Some(protobuf::parse_from_bytes(&p)?)) + } else { + Ok(None) + } + } +} diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 358c84c5d44..20d81431a21 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -19,9 +19,9 @@ // DEALINGS IN THE SOFTWARE. use futures::prelude::*; -use core::nodes::handled_node::NodeHandlerEndpoint; -use core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; -use core::{ConnectionUpgrade, Multiaddr, PeerId}; +use libp2p_core::nodes::handled_node::NodeHandlerEndpoint; +use libp2p_core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; +use libp2p_core::{ConnectionUpgrade, Multiaddr, PeerId}; use protocol::{RelayDestinationRequest, RelayHopRequest, RelayOutput, RelayConfig}; use std::{io, marker::PhantomData}; use tokio_io::{AsyncRead, AsyncWrite}; diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index 6899ebf5f64..5713783ca6a 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -19,6 +19,7 @@ // DEALINGS IN THE SOFTWARE. extern crate bytes; +#[macro_use] extern crate futures; extern crate libp2p_core; extern crate log; @@ -30,12 +31,11 @@ extern crate tokio_io; extern crate unsigned_varint; extern crate void; +mod codec; mod copy; -mod handler; mod error; mod message; -mod protocol; mod utility; -pub use handler::{RelayHandler, RelayHandlerEvent, RelayHandlerHopRequest, RelayHandlerDestRequest}; -pub use protocol::{RelayOutput, RelayConfig}; +//pub mod handler; +pub mod protocol; diff --git a/transports/relay/src/protocol.rs b/transports/relay/src/protocol.rs deleted file mode 100644 index 1b6c7634837..00000000000 --- a/transports/relay/src/protocol.rs +++ /dev/null @@ -1,433 +0,0 @@ -// 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 bytes::Bytes; -use copy; -use core::{upgrade, ConnectionUpgrade, Endpoint, Multiaddr, PeerId}; -use futures::{future::{self, Either::{A, B}, FutureResult}, prelude::*}; -use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; -use std::{io, iter}; -use tokio_io::{AsyncRead, AsyncWrite}; -use void::Void; - -/// Configuration for a connection upgrade that handles the relay protocol. -#[derive(Debug, Clone)] -pub struct RelayConfig { -} - -/// The `RelayConfig` upgrade can serve as destination or relay. Each mode needs a different -/// output type. As destination we want the stream to continue to be usable, whereas as relay -/// we pipe data from source to destination and do not want to use the stream in any other way. -/// Therefore, in the latter case we simply return a future that can be driven to completion -/// but otherwise the stream is not programmatically accessible. -// TODO: debug -pub enum RelayOutput { - /// We are successfully connected as a dialer, and we can now request the remote to act as a - /// proxy. - ProxyRequest(RelayProxyRequest), - /// We have been asked to become a destination. - DestinationRequest(RelayDestinationRequest), - /// We have been asked to relay communications to another node. - HopRequest(RelayHopRequest), -} - -/// We are successfully connected as a dialer, and we can now request the remote to act as a proxy. -// TODO: debug -#[must_use = "There is no point in opening a request if you don't use"] -pub struct RelayProxyRequest { - /// The stream of the destination. - stream: Io, -} - -impl RelayProxyRequest -where TStream: AsyncRead + AsyncWrite + 'static -{ - /// Request proxying to a destination. - pub fn request(self, dest_id: PeerId, dest_addresses: impl IntoIterator) - -> impl Future - { - let msg = hop_message(&Peer { - id: dest_id, - addrs: dest_addresses.into_iter().collect(), - }); - - self.stream - .send(msg) - .and_then(Io::recv) - .and_then(|(response, io)| { - let rsp = match response { - Some(m) => m, - None => return Err(io_err("no message from relay")), - }; - - if is_success(&rsp) { - Ok(io.into()) - } else { - Err(io_err("no success response from relay")) - } - }) - } -} - -/// Request from a remote for us to become a destination. -// TODO: debug -#[must_use = "A destination request should be either accepted or denied"] -pub struct RelayDestinationRequest { - /// The stream to the source. - stream: Io, - /// Source of the request. - from: Peer, -} - -impl RelayDestinationRequest -where TStream: AsyncRead + AsyncWrite + 'static -{ - /// Peer id of the source that is being relayed. - pub fn source_id(&self) -> &PeerId { - &self.from.id - } - - // TODO: addresses - - /// Accepts the request. - pub fn accept(self) -> impl Future { - // send a SUCCESS message - self.stream - .send(status(CircuitRelay_Status::SUCCESS)) - .and_then(Io::recv) - .and_then(|(response, io)| { - let rsp = match response { - Some(m) => m, - None => return Err(io_err("no message from destination")) - }; - - if is_success(&rsp) { - Ok(io.into()) - } else { - Err(io_err("no success response from relay")) - } - }) - } - - /// Refuses the request. - /// - /// The returned `Future` gracefully shuts down the request. - #[inline] - pub fn deny(self) -> impl Future { - // TODO: correct status - let message = deny_message(CircuitRelay_Status::STOP_RELAY_REFUSED); - self.stream.send(message).map(|_| ()) - } -} - -/// Request from a remote for us to relay communications to another node. -// TODO: debug -#[must_use = "A HOP request should be either accepted or denied"] -pub struct RelayHopRequest { - /// The stream to the source. - stream: Io, - /// Target of the request. - dest: Peer, -} - -impl RelayHopRequest -where TStream: AsyncRead + AsyncWrite + 'static -{ - /// Peer id of the node we should relay communications to. - #[inline] - pub fn target_id(&self) -> &PeerId { - &self.dest.id - } - - // TODO: addresses - - /// Accepts the request by providing a stream to the destination. - /// - /// The `dest_stream` should be a brand new dialing substream. This method will negotiate the - /// `relay` protocol on it, send a relay message, and then relay to it the connection from the - /// source. - /// - /// > **Note**: It is important that you process the `Future` that this method returns, - /// > otherwise the relaying will not work. - pub fn fulfill(self, dest_stream: TDestStream) -> impl Future - where TDestStream: AsyncRead + AsyncWrite + 'static - { - let source_stream = self.stream; - let stop = stop_message(&Peer::from_message(CircuitRelay_Peer::new()).unwrap(), &self.dest); - upgrade::apply(dest_stream, TrivialUpgrade, Endpoint::Dialer) - .and_then(|dest_stream| { - // send STOP message to destination and expect back a SUCCESS message - Io::new(dest_stream).send(stop) - .and_then(Io::recv) - .and_then(|(response, io)| { - let rsp = match response { - Some(m) => m, - None => return Err(io_err("no message from destination")) - }; - - if is_success(&rsp) { - Ok(io.into()) - } else { - Err(io_err("no success response from relay")) - } - }) - }) - // signal success or failure to source - .then(move |result| { - match result { - Ok(c) => { - let msg = status(CircuitRelay_Status::SUCCESS); - A(source_stream.send(msg).map(|io| (io.into(), c))) - } - Err(e) => { - let msg = status(CircuitRelay_Status::HOP_CANT_DIAL_DST); - B(source_stream.send(msg).and_then(|_| Err(e))) - } - } - }) - // return future for bidirectional data transfer - .and_then(move |(src, dst)| { - let (src_r, src_w) = src.split(); - let (dst_r, dst_w) = dst.split(); - let a = copy::flushing_copy(src_r, dst_w).map(|_| ()); - let b = copy::flushing_copy(dst_r, src_w).map(|_| ()); - a.select(b).map(|_| ()).map_err(|(e, _)| e) - }) - } - - /// Refuses the request. - /// - /// The returned `Future` gracefully shuts down the request. - #[inline] - pub fn deny(self) -> impl Future { - // TODO: correct status - let message = deny_message(CircuitRelay_Status::HOP_CANT_RELAY_TO_SELF); - self.stream.send(message).map(|_| ()) - } -} - -impl ConnectionUpgrade for RelayConfig -where - C: AsyncRead + AsyncWrite + Send + 'static, -{ - type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>; - type UpgradeIdentifier = (); - - fn protocol_names(&self) -> Self::NamesIter { - iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) - } - - type Output = RelayOutput; - type Future = Box + Send>; - - fn upgrade(self, conn: C, _: (), endpoint: Endpoint) -> Self::Future { - if let Endpoint::Dialer = endpoint { - let future = future::ok(RelayOutput::ProxyRequest(RelayProxyRequest { - stream: Io::new(conn), - })); - Box::new(future) - } else { - let future = Io::new(conn).recv().and_then(move |(message, io)| { - let msg = if let Some(m) = message { - m - } else { - return A(A(future::err(io_err("no message received")))) - }; - match msg.get_field_type() { - CircuitRelay_Type::HOP => { // act as relay - B(A(self.on_hop(msg, io))) - } - CircuitRelay_Type::STOP => { // act as destination - B(B(self.on_stop(msg, io))) - } - other => { - debug!("invalid message type: {:?}", other); - let resp = status(CircuitRelay_Status::MALFORMED_MESSAGE); - A(B(io.send(resp).and_then(|_| Err(io_err("invalid message type"))))) - } - } - }); - Box::new(future) - } - } -} - -impl RelayConfig { - /// Builds a new `RelayConfig` with default options. - pub fn new() -> RelayConfig { - RelayConfig {} - } - - /// HOP message handling (request to act as a relay). - fn on_hop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error=io::Error> - where - C: AsyncRead + AsyncWrite + 'static, - { - let from = if let Some(peer) = Peer::from_message(msg.take_srcPeer()) { - peer - } else { - let msg = status(CircuitRelay_Status::HOP_SRC_MULTIADDR_INVALID); - return B(A(io.send(msg).and_then(|_| Err(io_err("invalid src address"))))) - }; - - let dest = if let Some(peer) = Peer::from_message(msg.take_dstPeer()) { - peer - } else { - let msg = status(CircuitRelay_Status::HOP_DST_MULTIADDR_INVALID); - return B(B(io.send(msg).and_then(|_| Err(io_err("invalid dest address"))))) - }; - - A(future::ok(RelayOutput::HopRequest(RelayHopRequest { - stream: io, - dest, - }))) - } - - /// STOP message handling (we are a destination). - fn on_stop(self, mut msg: CircuitRelay, io: Io) -> impl Future, Error = io::Error> - where - C: AsyncRead + AsyncWrite + 'static, - { - let from = if let Some(peer) = Peer::from_message(msg.take_srcPeer()) { - peer - } else { - let msg = status(CircuitRelay_Status::HOP_SRC_MULTIADDR_INVALID); - return B(io.send(msg).and_then(|_| Err(io_err("invalid src address")))); - }; - - A(future::ok(RelayOutput::DestinationRequest(RelayDestinationRequest { - stream: io, - from, - }))) - } -} - -/// Generates a message that requests proxying. -fn hop_message(destination: &Peer) -> CircuitRelay { - let mut msg = CircuitRelay::new(); - msg.set_field_type(CircuitRelay_Type::HOP); - - let mut dest = CircuitRelay_Peer::new(); - dest.set_id(destination.id.as_bytes().to_vec()); - for a in &destination.addrs { - dest.mut_addrs().push(a.to_bytes()) - } - msg.set_dstPeer(dest); - - msg -} - -/// Generates a STOP message to send to a destination. -fn stop_message(from: &Peer, dest: &Peer) -> CircuitRelay { - let mut msg = CircuitRelay::new(); - msg.set_field_type(CircuitRelay_Type::STOP); - - let mut f = CircuitRelay_Peer::new(); - f.set_id(from.id.as_bytes().to_vec()); - for a in &from.addrs { - f.mut_addrs().push(a.to_bytes()) - } - msg.set_srcPeer(f); - - let mut d = CircuitRelay_Peer::new(); - d.set_id(dest.id.as_bytes().to_vec()); - for a in &dest.addrs { - d.mut_addrs().push(a.to_bytes()) - } - msg.set_dstPeer(d); - - msg -} - -/// Builds a message that refuses a HOP request. -fn deny_message(status: CircuitRelay_Status) -> CircuitRelay { - let mut msg = CircuitRelay::new(); - msg.set_field_type(CircuitRelay_Type::STATUS); // TODO: is this correct? - msg.set_code(status); - msg -} - -/// Dummy connection upgrade that negotiates the relay protocol and returns the socket. -#[derive(Debug, Clone)] -struct TrivialUpgrade; - -impl UpgradeInfo for TrivialUpgrade { - type UpgradeId = (); - type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; - - fn protocol_names(&self) -> Self::NamesIter { - iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) - } -} - -impl OutboundUpgrade for TrivialUpgrade -where - C: AsyncRead + AsyncWrite + 'static -{ - type Output = C; - type Error = Void; - type Future = FutureResult; - - fn upgrade_outbound(self, conn: C, _: ()) -> Self::Future { - future::ok(conn) - } -} - -/// Connection upgrade that negotiates the relay protocol, then negotiates with the target for it -/// to act as destination. -#[derive(Debug, Clone)] -pub(crate) struct Source(pub(crate) CircuitRelay); - -impl UpgradeInfo for Source { - type UpgradeId = (); - type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; - - fn protocol_names(&self) -> Self::NamesIter { - iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) - } -} - -impl OutboundUpgrade for Source -where - C: AsyncRead + AsyncWrite + Send + 'static, -{ - type Output = C; - type Error = io::Error; - type Future = Box + Send>; - - fn upgrade_outbound(self, conn: C, _: ()) -> Self::Future { - let future = Io::new(conn) - .send(self.0) - .and_then(Io::recv) - .and_then(|(response, io)| { - let rsp = match response { - Some(m) => m, - None => return Err(io_err("no message from relay")), - }; - if is_success(&rsp) { - Ok(io.into()) - } else { - Err(io_err("no success response from relay")) - } - }); - Box::new(future) - } -} - diff --git a/transports/relay/src/protocol/dest_request.rs b/transports/relay/src/protocol/dest_request.rs new file mode 100644 index 00000000000..fc4c2418f79 --- /dev/null +++ b/transports/relay/src/protocol/dest_request.rs @@ -0,0 +1,151 @@ +// 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 codec::Codec; +use libp2p_core::{Multiaddr, PeerId}; +use futures::prelude::*; +use message::{CircuitRelay, CircuitRelay_Status}; +use std::{error, io}; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; +use utility::{Peer, status}; // TODO: move these here + +/// Request from a remote for us to become a destination. +// TODO: debug +#[must_use = "A destination request should be either accepted or denied"] +pub struct RelayDestinationRequest { + /// The stream to the source. + stream: Framed, + /// Source of the request. + from: Peer, +} + +impl RelayDestinationRequest +where TStream: AsyncRead + AsyncWrite +{ + /// Creates a `RelayDestinationRequest`. + /// The `Framed` should be in the state right after pulling the remote's request message. + #[inline] + pub(crate) fn new(stream: Framed, from: Peer) -> Self { + RelayDestinationRequest { + stream, + from, + } + } + + /// Returns the peer id of the source that is being relayed. + #[inline] + pub fn source_id(&self) -> &PeerId { + &self.from.id + } + + /// Returns the addresses of the source that is being relayed. + #[inline] + pub fn source_addresses(&self) -> impl Iterator { + self.from.addrs.iter() + } + + /// Accepts the request. + /// + /// The returned `Future` sends back a success message then returns the raw stream. This raw + /// stream now points to the source (as retreived with `source_id()` and `source_addresses()`). + pub fn accept(self) -> RelayDestinationAcceptFuture { + let message = status(CircuitRelay_Status::SUCCESS); + RelayDestinationAcceptFuture { + inner: Some(self.stream), + message: Some(message), + } + } + + /// Refuses the request. + /// + /// The returned `Future` gracefully shuts down the request. + pub fn deny(self) -> RelayDestinationDenyFuture { + // TODO: correct status + let message = status(CircuitRelay_Status::STOP_RELAY_REFUSED); + RelayDestinationDenyFuture { + inner: self.stream, + message: Some(message), + } + } +} + +/// Future that accepts the request. +#[must_use = "futures do nothing unless polled"] +pub struct RelayDestinationAcceptFuture { + /// The inner stream. + inner: Option>, + /// The message to send to the remote. + message: Option, +} + +impl Future for RelayDestinationAcceptFuture +where TSubstream: AsyncRead + AsyncWrite, +{ + type Item = TSubstream; + type Error = Box; + + fn poll(&mut self) -> Poll { + if let Some(message) = self.message.take() { + match self.inner.as_mut().expect("Future is already finished").start_send(message)? { + AsyncSink::Ready => (), + AsyncSink::NotReady(message) => { + self.message = Some(message); + return Ok(Async::NotReady); + }, + } + } + + try_ready!(self.inner.as_mut().expect("Future is already finished").poll_complete()); + let stream = self.inner.take().expect("Future is already finished"); + Ok(Async::Ready(stream.into_inner())) // TODO: may be wrong because of caching + } +} + +/// Future that refuses the request. +#[must_use = "futures do nothing unless polled"] +pub struct RelayDestinationDenyFuture { + /// The inner stream. + inner: Framed, + /// The message to send to the remote. + message: Option, +} + +impl Future for RelayDestinationDenyFuture +where TSubstream: AsyncRead + AsyncWrite, +{ + type Item = (); + type Error = Box; + + fn poll(&mut self) -> Poll { + if let Some(message) = self.message.take() { + match self.inner.start_send(message)? { + AsyncSink::Ready => (), + AsyncSink::NotReady(message) => { + self.message = Some(message); + return Ok(Async::NotReady); + }, + } + } + + try_ready!(self.inner.poll_complete()); + Ok(Async::Ready(())) + } +} diff --git a/transports/relay/src/protocol/hop_request.rs b/transports/relay/src/protocol/hop_request.rs new file mode 100644 index 00000000000..b201751d4bd --- /dev/null +++ b/transports/relay/src/protocol/hop_request.rs @@ -0,0 +1,200 @@ +// 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 codec::Codec; +use libp2p_core::{Multiaddr, PeerId}; +use futures::prelude::*; +use message::{CircuitRelay, CircuitRelay_Status}; +use std::error; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; +use utility::{Peer, status}; // TODO: move these here + +/// Request from a remote for us to relay communications to another node. +// TODO: debug +#[must_use = "A HOP request should be either accepted or denied"] +pub struct RelayHopRequest { + /// The stream to the source. + stream: Framed, + /// Target of the request. + dest: Peer, +} + +impl RelayHopRequest +where TStream: AsyncRead + AsyncWrite +{ + /// Creates a `RelayHopRequest`. + /// The `Framed` should be in the state right after pulling the remote's request message. + #[inline] + pub(crate) fn new(stream: Framed, dest: Peer) -> Self { + RelayHopRequest { + stream, + dest, + } + } + + /// Peer id of the node we should relay communications to. + #[inline] + pub fn target_id(&self) -> &PeerId { + &self.dest.id + } + + /// Returns the addresses of the target, as reported by the requester. + #[inline] + pub fn target_addresses(&self) -> impl Iterator { + self.dest.addrs.iter() + } + + /// Accepts the request by providing a stream to the destination. + /// + /// The `dest_stream` should be a brand new dialing substream. This method will negotiate the + /// `relay` protocol on it, send a relay message, and then relay to it the connection from the + /// source. + /// + /// > **Note**: It is important that you process the `Future` that this method returns, + /// > otherwise the relaying will not work. + pub fn fulfill(self, dest_stream: TDestStream) -> RelayHopAcceptFuture + where TDestStream: AsyncRead + AsyncWrite + { + unimplemented!() // TODO: + /*RelayHopAcceptFuture { + inner: Some(self.stream), + message: Some(message), + } + + let source_stream = self.stream; + let stop = stop_message(&Peer::from_message(CircuitRelay_Peer::new()).unwrap(), &self.dest); + upgrade::apply(dest_stream, TrivialUpgrade, Endpoint::Dialer) + .and_then(|dest_stream| { + // send STOP message to destination and expect back a SUCCESS message + Io::new(dest_stream).send(stop) + .and_then(Io::recv) + .and_then(|(response, io)| { + let rsp = match response { + Some(m) => m, + None => return Err(io_err("no message from destination")) + }; + + if is_success(&rsp) { + Ok(io.into()) + } else { + Err(io_err("no success response from relay")) + } + }) + }) + // signal success or failure to source + .then(move |result| { + match result { + Ok(c) => { + let msg = status(CircuitRelay_Status::SUCCESS); + A(source_stream.send(msg).map(|io| (io.into(), c))) + } + Err(e) => { + let msg = status(CircuitRelay_Status::HOP_CANT_DIAL_DST); + B(source_stream.send(msg).and_then(|_| Err(e))) + } + } + }) + // return future for bidirectional data transfer + .and_then(move |(src, dst)| { + let (src_r, src_w) = src.split(); + let (dst_r, dst_w) = dst.split(); + let a = copy::flushing_copy(src_r, dst_w).map(|_| ()); + let b = copy::flushing_copy(dst_r, src_w).map(|_| ()); + a.select(b).map(|_| ()).map_err(|(e, _)| e) + })*/ + } + + /// Refuses the request. + /// + /// The returned `Future` gracefully shuts down the request. + #[inline] + pub fn deny(self) -> RelayHopDenyFuture { + // TODO: correct status + let message = status(CircuitRelay_Status::HOP_CANT_RELAY_TO_SELF); + RelayHopDenyFuture { + inner: self.stream, + message: Some(message), + } + } +} + +/// Future that accepts the request. +#[must_use = "futures do nothing unless polled"] +pub struct RelayHopAcceptFuture { + /// The inner stream. + inner: Option>, + /// The message to send to the remote. + message: Option, +} + +impl Future for RelayHopAcceptFuture +where TSubstream: AsyncRead + AsyncWrite, +{ + type Item = TSubstream; + type Error = Box; + + fn poll(&mut self) -> Poll { + if let Some(message) = self.message.take() { + match self.inner.as_mut().expect("Future is already finished").start_send(message)? { + AsyncSink::Ready => (), + AsyncSink::NotReady(message) => { + self.message = Some(message); + return Ok(Async::NotReady); + }, + } + } + + try_ready!(self.inner.as_mut().expect("Future is already finished").poll_complete()); + let stream = self.inner.take().expect("Future is already finished"); + Ok(Async::Ready(stream.into_inner())) // TODO: may be wrong because of caching + } +} + +/// Future that refuses the request. +#[must_use = "futures do nothing unless polled"] +pub struct RelayHopDenyFuture { + /// The inner stream. + inner: Framed, + /// The message to send to the remote. + message: Option, +} + +impl Future for RelayHopDenyFuture +where TSubstream: AsyncRead + AsyncWrite, +{ + type Item = (); + type Error = Box; + + fn poll(&mut self) -> Poll { + if let Some(message) = self.message.take() { + match self.inner.start_send(message)? { + AsyncSink::Ready => (), + AsyncSink::NotReady(message) => { + self.message = Some(message); + return Ok(Async::NotReady); + }, + } + } + + try_ready!(self.inner.poll_complete()); + Ok(Async::Ready(())) + } +} diff --git a/transports/relay/src/protocol/listen.rs b/transports/relay/src/protocol/listen.rs new file mode 100644 index 00000000000..b75fa7b9d94 --- /dev/null +++ b/transports/relay/src/protocol/listen.rs @@ -0,0 +1,154 @@ +// 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 bytes::Bytes; +use codec::Codec; +use libp2p_core::upgrade; +use futures::prelude::*; +use message::CircuitRelay_Type; +use protocol::{dest_request::RelayDestinationRequest, hop_request::RelayHopRequest}; +use std::{error, fmt, io, iter}; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; +use utility::Peer; // TODO: move + +/// Configuration for an inbound upgrade that handles requests from the remote for the relay +/// protocol. +#[derive(Debug, Clone)] +pub struct RelayListen { +} + +pub enum RelayRemoteRequest { + /// We have been asked to become a destination. + DestinationRequest(RelayDestinationRequest), + /// We have been asked to relay communications to another node. + HopRequest(RelayHopRequest), +} + +impl RelayListen { + /// Builds a new `RelayListen` with default options. + pub fn new() -> RelayListen { + RelayListen {} + } +} + +impl upgrade::UpgradeInfo for RelayListen { + type UpgradeId = (); + type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; + + fn protocol_names(&self) -> Self::NamesIter { + iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) + } +} + +impl upgrade::InboundUpgrade for RelayListen +where + TSubstream: AsyncRead + AsyncWrite, +{ + type Output = RelayRemoteRequest; + type Error = RelayListenError; + type Future = RelayListenFuture; + + fn upgrade_inbound(self, conn: TSubstream, _: ()) -> Self::Future { + let inner = Framed::new(conn, Codec::new()); + RelayListenFuture { + inner: Some(inner) + } + } +} + +/// Future that negotiates an inbound substream for the relay protocol. +#[must_use = "futures do nothing unless polled"] +pub struct RelayListenFuture { + /// Inner stream. + inner: Option>, +} + +impl Future for RelayListenFuture +where TSubstream: AsyncRead + AsyncWrite, +{ + type Item = RelayRemoteRequest; + type Error = RelayListenError; + + fn poll(&mut self) -> Poll { + let mut msg = match try_ready!(self.inner.as_mut().expect("Future already finished").poll()) { + Some(msg) => msg, + None => return Err(RelayListenError::UnexpectedEof), + }; + + match msg.get_field_type() { + CircuitRelay_Type::HOP => { + let peer = Peer::from_message(msg.take_dstPeer()); + let rq = RelayHopRequest::new(self.inner.take().unwrap(), peer.unwrap()); // TODO: + Ok(Async::Ready(RelayRemoteRequest::HopRequest(rq))) + }, + CircuitRelay_Type::STOP => { + let peer = Peer::from_message(msg.take_srcPeer()); + let rq = RelayDestinationRequest::new(self.inner.take().unwrap(), peer.unwrap()); // TODO: + Ok(Async::Ready(RelayRemoteRequest::DestinationRequest(rq))) + }, + _ => Err(RelayListenError::InvalidMessageTy) + } + } +} + +/// Error while upgrading with a `RelayListen`. +#[derive(Debug)] +pub enum RelayListenError { + /// Raw error on the substream. + Io(Box), + /// The remote closed the connection before we could get a message. + UnexpectedEof, + /// Received a message invalid in this context. + InvalidMessageTy, +} + +impl fmt::Display for RelayListenError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + RelayListenError::Io(ref err) => { + write!(f, "Raw error on the substream: {:?}", err) + }, + RelayListenError::UnexpectedEof => { + write!(f, "The remote closed the connection before we could get a message") + }, + RelayListenError::InvalidMessageTy => { + write!(f, "Received a message invalid in this context.") + }, + } + } +} + +impl error::Error for RelayListenError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + RelayListenError::Io(ref err) => Some(&**err), + RelayListenError::UnexpectedEof => None, + RelayListenError::InvalidMessageTy => None, + } + } +} + +impl From> for RelayListenError { + #[inline] + fn from(err: Box) -> Self { + RelayListenError::Io(err) + } +} diff --git a/transports/relay/src/protocol/mod.rs b/transports/relay/src/protocol/mod.rs new file mode 100644 index 00000000000..45d3031ae46 --- /dev/null +++ b/transports/relay/src/protocol/mod.rs @@ -0,0 +1,25 @@ +// 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. + +mod dest_request; +mod hop_request; +mod listen; +mod relay_request; +mod target_open; diff --git a/transports/relay/src/protocol/relay_request.rs b/transports/relay/src/protocol/relay_request.rs new file mode 100644 index 00000000000..b42ebfc0dca --- /dev/null +++ b/transports/relay/src/protocol/relay_request.rs @@ -0,0 +1,140 @@ +// 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 bytes::Bytes; +use codec::Codec; +use libp2p_core::{upgrade, Multiaddr, PeerId}; +use futures::prelude::*; +use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Type}; +use std::{error, io, iter}; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; + +/// Request to act as a relay to a third party. +/// +/// If we take a destination where a *source* wants to talk to a *destination* through a *relay*, +/// this struct is the message that the *source* sends to the *relay* at initialization. The +/// parameters passed to `RelayProxyRequest::new()` are the information of the *destination*. +/// +/// The upgrade should be performed on a substream to the *relay*. +/// +/// If the upgrade succeeds, the substream is returned and is now a brand new connection pointing +/// to the *destination*. +// TODO: debug +pub struct RelayProxyRequest { + /// Message that we will send to the relay. Prepared in advance. + message: CircuitRelay, +} + +impl RelayProxyRequest { + /// Builds a request for the target to act as a relay to a third party. + pub fn new(dest_id: PeerId, dest_addresses: impl IntoIterator) -> Self { + let mut msg = CircuitRelay::new(); + msg.set_field_type(CircuitRelay_Type::HOP); + + let mut dest = CircuitRelay_Peer::new(); + dest.set_id(dest_id.as_bytes().to_vec()); + for a in dest_addresses { + dest.mut_addrs().push(a.to_bytes()) + } + msg.set_dstPeer(dest); + + RelayProxyRequest { + message: msg, + } + } +} + +impl upgrade::UpgradeInfo for RelayProxyRequest { + type UpgradeId = (); + type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; + + #[inline] + fn protocol_names(&self) -> Self::NamesIter { + iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) + } +} + +impl upgrade::OutboundUpgrade for RelayProxyRequest +where + TSubstream: AsyncRead + AsyncWrite, +{ + type Output = TSubstream; + type Error = Box; + type Future = RelayProxyRequestFuture; + + #[inline] + fn upgrade_outbound(self, conn: TSubstream, _: ()) -> Self::Future { + let framed = Framed::new(conn, Codec::new()); + RelayProxyRequestFuture { + stream: framed, + message: Some(self.message), + flushed: false, + } + } +} + +/// Future that drives upgrading the `RelayProxyRequest`. +// TODO: Debug +#[must_use = "futures do nothing unless polled"] +pub struct RelayProxyRequestFuture { + /// The stream to the relay. + stream: Framed, + + /// Message to send to the relay, or `None` if it has been successfully sent. + message: Option, + + /// If true, we successfully flushed after sending. + flushed: bool, +} + +impl Future for RelayProxyRequestFuture +where TSubstream: AsyncRead + AsyncWrite, +{ + type Item = TSubstream; + type Error = Box; + + fn poll(&mut self) -> Poll { + if let Some(message) = self.message.take() { + match self.stream.start_send(message)? { + AsyncSink::Ready => (), + AsyncSink::NotReady(message) => { + self.message = Some(message); + return Ok(Async::NotReady); + }, + } + } + + if !self.flushed { + try_ready!(self.stream.poll_complete()); + self.flushed = true; + } + + panic!() // FIXME: + /*match try_ready!(self.stream.poll()) { + Some(message) => { + + }, + None => { + + }, + }*/ + } +} diff --git a/transports/relay/src/protocol/target_open.rs b/transports/relay/src/protocol/target_open.rs new file mode 100644 index 00000000000..d6501ae4680 --- /dev/null +++ b/transports/relay/src/protocol/target_open.rs @@ -0,0 +1,88 @@ +// 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 bytes::Bytes; +use codec::Codec; +use copy; +use libp2p_core::{upgrade, Endpoint, Multiaddr, PeerId}; +use futures::{future::{self, Either::{A, B}, FutureResult}, prelude::*}; +use message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; +use std::{io, iter}; +use tokio_codec::Framed; +use tokio_io::{AsyncRead, AsyncWrite}; +use utility::{Peer, status}; // TODO: move these here +use void::Void; + +/// Upgrade that negotiates the relay protocol and returns the socket. +#[derive(Debug, Clone)] +pub struct TargetOpen { + /// The message to send to the destination. Pre-computed. + message: CircuitRelay, +} + +impl TargetOpen { + /// Creates a `TargetOpen`. Must pass the parameters of the message. + pub(crate) fn new(from: Peer, dest: Peer) -> Self { + let mut msg = CircuitRelay::new(); + msg.set_field_type(CircuitRelay_Type::STOP); + + let mut f = CircuitRelay_Peer::new(); + f.set_id(from.id.as_bytes().to_vec()); + for a in &from.addrs { + f.mut_addrs().push(a.to_bytes()) + } + msg.set_srcPeer(f); + + let mut d = CircuitRelay_Peer::new(); + d.set_id(dest.id.as_bytes().to_vec()); + for a in &dest.addrs { + d.mut_addrs().push(a.to_bytes()) + } + msg.set_dstPeer(d); + + TargetOpen { + message: msg, + } + } +} + +impl upgrade::UpgradeInfo for TargetOpen { + type UpgradeId = (); + type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; + + #[inline] + fn protocol_names(&self) -> Self::NamesIter { + iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) + } +} + +impl upgrade::OutboundUpgrade for TargetOpen +where + TSubstream: AsyncRead + AsyncWrite, +{ + type Output = TSubstream; + type Error = Void; + type Future = FutureResult; + + #[inline] + fn upgrade_outbound(self, conn: TSubstream, _: ()) -> Self::Future { + future::ok(conn) + } +} diff --git a/transports/relay/src/utility.rs b/transports/relay/src/utility.rs index ba342307a1e..5e48d937fa0 100644 --- a/transports/relay/src/utility.rs +++ b/transports/relay/src/utility.rs @@ -20,6 +20,7 @@ use crate::message::{CircuitRelay, CircuitRelay_Peer, CircuitRelay_Status, CircuitRelay_Type}; use futures::{future::{self, Either}, prelude::*}; +use libp2p_core::PeerId; use log::trace; use multiaddr::{Protocol, Multiaddr}; use protobuf::{self, Message}; From ebcd209a9cfc91958dfbad2f9e917f936c66c061 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 28 Nov 2018 19:56:06 +0100 Subject: [PATCH 20/23] Minor work --- transports/relay/src/handler.rs | 10 ++++------ transports/relay/src/lib.rs | 2 +- transports/relay/src/protocol/mod.rs | 5 +++++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 20d81431a21..324df521636 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -22,15 +22,12 @@ use futures::prelude::*; use libp2p_core::nodes::handled_node::NodeHandlerEndpoint; use libp2p_core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; use libp2p_core::{ConnectionUpgrade, Multiaddr, PeerId}; -use protocol::{RelayDestinationRequest, RelayHopRequest, RelayOutput, RelayConfig}; +use protocol; use std::{io, marker::PhantomData}; use tokio_io::{AsyncRead, AsyncWrite}; /// Protocol handler that identifies the remote at a regular period. pub struct RelayHandler { - /// Configuration for the protocol. - config: RelayConfig, - /// True if wanting to shut down. shutdown: bool, @@ -149,13 +146,14 @@ where type InEvent = RelayHandlerIn; type OutEvent = RelayHandlerEvent; type Substream = TSubstream; - type Protocol = RelayConfig; + type InboundProtocol = protocol::RelayListen; + type OutboundProtocol = RelayConfig; // The information is the peer we want to relay communications to. type OutboundOpenInfo = (PeerId, Vec); #[inline] fn listen_protocol(&self) -> Self::Protocol { - self.config.clone() + protocol::RelayListen::new() } fn inject_fully_negotiated( diff --git a/transports/relay/src/lib.rs b/transports/relay/src/lib.rs index 5713783ca6a..a60cfd20629 100644 --- a/transports/relay/src/lib.rs +++ b/transports/relay/src/lib.rs @@ -37,5 +37,5 @@ mod error; mod message; mod utility; -//pub mod handler; +pub mod handler; pub mod protocol; diff --git a/transports/relay/src/protocol/mod.rs b/transports/relay/src/protocol/mod.rs index 45d3031ae46..42f37a0f45d 100644 --- a/transports/relay/src/protocol/mod.rs +++ b/transports/relay/src/protocol/mod.rs @@ -23,3 +23,8 @@ mod hop_request; mod listen; mod relay_request; mod target_open; + +pub use self::dest_request::*; +pub use self::hop_request::*; +pub use self::listen::*; +pub use self::relay_request::*; From 28dddc1f685ccee9da36d91be034b024b51f71c3 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 29 Nov 2018 17:17:12 +0100 Subject: [PATCH 21/23] More work --- transports/relay/src/handler.rs | 69 +++++++++----------- transports/relay/src/protocol/mod.rs | 2 + transports/relay/src/protocol/target_open.rs | 12 ++-- 3 files changed, 39 insertions(+), 44 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 324df521636..45b5fa544e7 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -19,9 +19,8 @@ // DEALINGS IN THE SOFTWARE. use futures::prelude::*; -use libp2p_core::nodes::handled_node::NodeHandlerEndpoint; -use libp2p_core::nodes::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; -use libp2p_core::{ConnectionUpgrade, Multiaddr, PeerId}; +use libp2p_core::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; +use libp2p_core::{Multiaddr, PeerId}; use protocol; use std::{io, marker::PhantomData}; use tokio_io::{AsyncRead, AsyncWrite}; @@ -55,7 +54,7 @@ pub enum RelayHandlerEvent { DestinationRequest(RelayHandlerDestRequest), } -/// Event produced by the relay handler. +/// Event that can be sent to the relay handler. //#[derive(Debug)] // TODO: restore pub enum RelayHandlerIn { /// Accept a hop request from the remote. @@ -67,6 +66,8 @@ pub enum RelayHandlerIn { }, /// Denies a hop request from the remote. DenyHopRequest(RelayHandlerHopRequest), + /// Denies a destination request from the remote. + DenyDestinationRequest(RelayHandlerDestRequest), /// Opens a new substream to the remote and asks it to relay communications to a third party. RelayRequest { /// Id of the peer to connect to. @@ -115,12 +116,6 @@ where TSubstream: AsyncRead + AsyncWrite + 'static pub fn accept(self) -> impl Future { self.inner.accept() } - - /// Denies the request. Produces a `Future` that sends back an error message to the proxyy. - #[inline] - pub fn deny(self) -> impl Future { - self.inner.deny() - } } impl RelayHandler { @@ -128,7 +123,6 @@ impl RelayHandler { #[inline] pub fn new() -> Self { RelayHandler { - config: RelayConfig::new(), shutdown: false, active_futures: Vec::new(), relay_requests: Vec::new(), @@ -147,42 +141,41 @@ where type OutEvent = RelayHandlerEvent; type Substream = TSubstream; type InboundProtocol = protocol::RelayListen; - type OutboundProtocol = RelayConfig; - // The information is the peer we want to relay communications to. - type OutboundOpenInfo = (PeerId, Vec); + type OutboundProtocol = EitherUpgrade; + type OutboundOpenInfo = (); #[inline] - fn listen_protocol(&self) -> Self::Protocol { + fn listen_protocol(&self) -> Self::InboundProtocol { protocol::RelayListen::new() } - fn inject_fully_negotiated( + fn inject_fully_negotiated_inbound( &mut self, - protocol: >::Output, - endpoint: NodeHandlerEndpoint, + protocol: >::Output, ) { - match (protocol, endpoint) { - (RelayOutput::HopRequest(inner), _) => { - // The remote wants us to relay communications to a third party. - let ev = RelayHandlerEvent::HopRequest(RelayHandlerHopRequest { - inner, - }); - self.queued_events.push(ev); + match protocol { + // We have been asked to become a destination. + RelayRemoteRequest::DestinationRequest(dest_request) => { + }, - (RelayOutput::DestinationRequest(inner), _) => { - // The remote wants to use us as a destination. - let ev = RelayHandlerEvent::DestinationRequest(RelayHandlerDestRequest { - inner, - }); - self.queued_events.push(ev); + // We have been asked to act as a proxy. + RelayRemoteRequest::HopRequest(hop_request) => { + }, - (RelayOutput::ProxyRequest(request), NodeHandlerEndpoint::Dialer((peer_id, addresses))) => { - // We can ask the remote for what we want it to do. - request.request(peer_id, addresses); + } + } + + fn inject_fully_negotiated_outbound( + &mut self, + protocol: >::Output, + _: Self::OutboundOpenInfo, + ) { + match protocol { + EitherOutput::First(proxy_request) => { + }, - (RelayOutput::ProxyRequest(_), NodeHandlerEndpoint::Listener) => { - // This shouldn't happen. - debug_assert!(false, "the protocol forbids ProxyRequest when listening"); + EitherOutput::Second(target_request) => { + }, } } @@ -222,7 +215,7 @@ where ) -> Poll< Option< ProtocolsHandlerEvent< - Self::Protocol, + Self::OutboundProtocol, Self::OutboundOpenInfo, RelayHandlerEvent, >, diff --git a/transports/relay/src/protocol/mod.rs b/transports/relay/src/protocol/mod.rs index 42f37a0f45d..970193ea06b 100644 --- a/transports/relay/src/protocol/mod.rs +++ b/transports/relay/src/protocol/mod.rs @@ -24,7 +24,9 @@ mod listen; mod relay_request; mod target_open; +// TODO: exact reexports pub use self::dest_request::*; pub use self::hop_request::*; pub use self::listen::*; pub use self::relay_request::*; +pub use self::target_open::RelayTargetOpen; diff --git a/transports/relay/src/protocol/target_open.rs b/transports/relay/src/protocol/target_open.rs index d6501ae4680..c6fd71193c8 100644 --- a/transports/relay/src/protocol/target_open.rs +++ b/transports/relay/src/protocol/target_open.rs @@ -32,13 +32,13 @@ use void::Void; /// Upgrade that negotiates the relay protocol and returns the socket. #[derive(Debug, Clone)] -pub struct TargetOpen { +pub struct RelayTargetOpen { /// The message to send to the destination. Pre-computed. message: CircuitRelay, } -impl TargetOpen { - /// Creates a `TargetOpen`. Must pass the parameters of the message. +impl RelayTargetOpen { + /// Creates a `RelayTargetOpen`. Must pass the parameters of the message. pub(crate) fn new(from: Peer, dest: Peer) -> Self { let mut msg = CircuitRelay::new(); msg.set_field_type(CircuitRelay_Type::STOP); @@ -57,13 +57,13 @@ impl TargetOpen { } msg.set_dstPeer(d); - TargetOpen { + RelayTargetOpen { message: msg, } } } -impl upgrade::UpgradeInfo for TargetOpen { +impl upgrade::UpgradeInfo for RelayTargetOpen { type UpgradeId = (); type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; @@ -73,7 +73,7 @@ impl upgrade::UpgradeInfo for TargetOpen { } } -impl upgrade::OutboundUpgrade for TargetOpen +impl upgrade::OutboundUpgrade for RelayTargetOpen where TSubstream: AsyncRead + AsyncWrite, { From 7326ca7d9d4389a8e717594c8dffab8e25bf3bf2 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 4 Dec 2018 12:05:16 +0100 Subject: [PATCH 22/23] More work --- transports/relay/src/handler.rs | 56 ++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index 45b5fa544e7..e6a5b1be381 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -20,13 +20,13 @@ use futures::prelude::*; use libp2p_core::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; -use libp2p_core::{Multiaddr, PeerId}; +use libp2p_core::{Multiaddr, PeerId, either, upgrade}; use protocol; -use std::{io, marker::PhantomData}; +use std::{error, io, marker::PhantomData}; use tokio_io::{AsyncRead, AsyncWrite}; /// Protocol handler that identifies the remote at a regular period. -pub struct RelayHandler { +pub struct RelayHandler { /// True if wanting to shut down. shutdown: bool, @@ -41,7 +41,7 @@ pub struct RelayHandler { queued_events: Vec>, /// Phantom data. - marker: PhantomData + marker: PhantomData<(TDestSubstream, TSrcSubstream)> } /// Event produced by the relay handler. @@ -49,6 +49,7 @@ pub struct RelayHandler { pub enum RelayHandlerEvent { /// The remote wants us to relay communications to a third party. HopRequest(RelayHandlerHopRequest), + /// The remote is a relay and is relaying a connection to us. In other words, we are used as /// destination. DestinationRequest(RelayHandlerDestRequest), @@ -56,7 +57,7 @@ pub enum RelayHandlerEvent { /// Event that can be sent to the relay handler. //#[derive(Debug)] // TODO: restore -pub enum RelayHandlerIn { +pub enum RelayHandlerIn { /// Accept a hop request from the remote. AcceptHopRequest { /// The request that was produced by the handler earlier. @@ -64,10 +65,13 @@ pub enum RelayHandlerIn { /// The substream to the destination. dest_substream: TDestSubstream, }, + /// Denies a hop request from the remote. DenyHopRequest(RelayHandlerHopRequest), + /// Denies a destination request from the remote. DenyDestinationRequest(RelayHandlerDestRequest), + /// Opens a new substream to the remote and asks it to relay communications to a third party. RelayRequest { /// Id of the peer to connect to. @@ -75,11 +79,21 @@ pub enum RelayHandlerIn { /// Addresses known for this peer. addresses: Vec, }, + + /// Asks the node to be used as a destination for a relayed connection. + DestinationRequest { + /// Peer id of the node whose communications are being relayed. + source: PeerId, + /// Addresses of the node whose communications are being relayed. + source_addresses: Vec, + /// Substream to the source. + substream: TSrcSubstream, + }, } /// The remote wants us to be treated as a destination. pub struct RelayHandlerHopRequest { - inner: RelayHopRequest, + inner: protocol::RelayHopRequest, } impl RelayHandlerHopRequest @@ -96,7 +110,7 @@ where TSubstream: AsyncRead + AsyncWrite + 'static /// The remote wants us to be treated as a destination. pub struct RelayHandlerDestRequest { - inner: RelayDestinationRequest, + inner: protocol::RelayDestinationRequest, } impl RelayHandlerDestRequest @@ -108,17 +122,21 @@ where TSubstream: AsyncRead + AsyncWrite + 'static self.inner.source_id() } - // TODO: addresses + /// Addresses of the source that is being relayed. + #[inline] + pub fn source_addresses(&self) -> &PeerId { + self.inner.source_addresses() + } /// Accepts the request. Produces a `Future` that sends back a success message then provides /// the stream to the source. #[inline] - pub fn accept(self) -> impl Future { + pub fn accept(self) -> impl Future> { self.inner.accept() } } -impl RelayHandler { +impl RelayHandler { /// Builds a new `RelayHandler`. #[inline] pub fn new() -> Self { @@ -132,16 +150,16 @@ impl RelayHandler { } } -impl ProtocolsHandler for RelayHandler +impl ProtocolsHandler for RelayHandler where TSubstream: AsyncRead + AsyncWrite + Send + Sync + 'static, // TODO: remove useless bounds TDestSubstream: AsyncRead + AsyncWrite + Send + Sync + 'static, // TODO: remove useless bounds { - type InEvent = RelayHandlerIn; + type InEvent = RelayHandlerIn; type OutEvent = RelayHandlerEvent; type Substream = TSubstream; type InboundProtocol = protocol::RelayListen; - type OutboundProtocol = EitherUpgrade; + type OutboundProtocol = upgrade::EitherUpgrade; type OutboundOpenInfo = (); #[inline] @@ -151,15 +169,15 @@ where fn inject_fully_negotiated_inbound( &mut self, - protocol: >::Output, + protocol: >::Output, ) { match protocol { // We have been asked to become a destination. - RelayRemoteRequest::DestinationRequest(dest_request) => { + protocol::RelayRemoteRequest::DestinationRequest(dest_request) => { }, // We have been asked to act as a proxy. - RelayRemoteRequest::HopRequest(hop_request) => { + protocol::RelayRemoteRequest::HopRequest(hop_request) => { }, } @@ -167,14 +185,14 @@ where fn inject_fully_negotiated_outbound( &mut self, - protocol: >::Output, + protocol: >::Output, _: Self::OutboundOpenInfo, ) { match protocol { - EitherOutput::First(proxy_request) => { + either::EitherOutput::First(proxy_request) => { }, - EitherOutput::Second(target_request) => { + either::EitherOutput::Second(target_request) => { }, } From abe8ac79a08696dec87c4308da31fcd0d1ef9e7f Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Sat, 19 Jan 2019 20:28:11 +0100 Subject: [PATCH 23/23] More work towards compilation --- transports/relay/src/handler.rs | 30 +++++++++++-------- transports/relay/src/protocol/listen.rs | 11 +++---- .../relay/src/protocol/relay_request.rs | 10 +++---- transports/relay/src/protocol/target_open.rs | 10 +++---- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/transports/relay/src/handler.rs b/transports/relay/src/handler.rs index e6a5b1be381..7bc5868208f 100644 --- a/transports/relay/src/handler.rs +++ b/transports/relay/src/handler.rs @@ -19,8 +19,8 @@ // DEALINGS IN THE SOFTWARE. use futures::prelude::*; -use libp2p_core::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent}; -use libp2p_core::{Multiaddr, PeerId, either, upgrade}; +use libp2p_core::protocols_handler::{ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr}; +use libp2p_core::{Multiaddr, PeerId, either, upgrade, upgrade::OutboundUpgrade}; use protocol; use std::{error, io, marker::PhantomData}; use tokio_io::{AsyncRead, AsyncWrite}; @@ -157,6 +157,7 @@ where { type InEvent = RelayHandlerIn; type OutEvent = RelayHandlerEvent; + type Error = io::Error; type Substream = TSubstream; type InboundProtocol = protocol::RelayListen; type OutboundProtocol = upgrade::EitherUpgrade; @@ -219,10 +220,15 @@ where fn inject_inbound_closed(&mut self) {} #[inline] - fn inject_dial_upgrade_error(&mut self, _: Self::OutboundOpenInfo, _: io::Error) { + fn inject_dial_upgrade_error(&mut self, _: Self::OutboundOpenInfo, _: ProtocolsHandlerUpgrErr<>::Error>) { // TODO: report? } + #[inline] + fn connection_keep_alive(&self) -> bool { + true // TODO: + } + #[inline] fn shutdown(&mut self) { self.shutdown = true; @@ -231,28 +237,26 @@ where fn poll( &mut self, ) -> Poll< - Option< - ProtocolsHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - RelayHandlerEvent, - >, + ProtocolsHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + RelayHandlerEvent, >, io::Error, > { // Open substreams if necessary. if !self.relay_requests.is_empty() { let rq = self.relay_requests.remove(0); - return Ok(Async::Ready(Some(ProtocolsHandlerEvent::OutboundSubstreamRequest { + return Ok(Async::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { info: rq, upgrade: self.config.clone(), - }))); + })); } // Report the queued events. if !self.queued_events.is_empty() { let event = self.queued_events.remove(0); - return Ok(Async::Ready(Some(ProtocolsHandlerEvent::Custom(event)))); + return Ok(Async::Ready(ProtocolsHandlerEvent::Custom(event))); } // We remove each element from `active_futures` one by one and add them back. @@ -268,7 +272,7 @@ where // Shut down process. if self.shutdown { self.active_futures.clear(); // TODO: not a proper shutdown - return Ok(Async::Ready(None)); + return Ok(Async::Ready(ProtocolsHandlerEvent::Shutdown)); } Ok(Async::NotReady) diff --git a/transports/relay/src/protocol/listen.rs b/transports/relay/src/protocol/listen.rs index b75fa7b9d94..400c3a695bc 100644 --- a/transports/relay/src/protocol/listen.rs +++ b/transports/relay/src/protocol/listen.rs @@ -50,11 +50,12 @@ impl RelayListen { } impl upgrade::UpgradeInfo for RelayListen { - type UpgradeId = (); - type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; + type Info = &'static [u8]; + type InfoIter = iter::Once; - fn protocol_names(&self) -> Self::NamesIter { - iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) + #[inline] + fn protocol_info(&self) -> Self::InfoIter { + iter::once("/libp2p/relay/circuit/0.1.0") } } @@ -66,7 +67,7 @@ where type Error = RelayListenError; type Future = RelayListenFuture; - fn upgrade_inbound(self, conn: TSubstream, _: ()) -> Self::Future { + fn upgrade_inbound(self, conn: TSubstream, _: Self::Info) -> Self::Future { let inner = Framed::new(conn, Codec::new()); RelayListenFuture { inner: Some(inner) diff --git a/transports/relay/src/protocol/relay_request.rs b/transports/relay/src/protocol/relay_request.rs index b42ebfc0dca..11ee6ff5b83 100644 --- a/transports/relay/src/protocol/relay_request.rs +++ b/transports/relay/src/protocol/relay_request.rs @@ -63,12 +63,12 @@ impl RelayProxyRequest { } impl upgrade::UpgradeInfo for RelayProxyRequest { - type UpgradeId = (); - type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; + type Info = &'static [u8]; + type InfoIter = iter::Once; #[inline] - fn protocol_names(&self) -> Self::NamesIter { - iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) + fn protocol_info(&self) -> Self::InfoIter { + iter::once("/libp2p/relay/circuit/0.1.0") } } @@ -81,7 +81,7 @@ where type Future = RelayProxyRequestFuture; #[inline] - fn upgrade_outbound(self, conn: TSubstream, _: ()) -> Self::Future { + fn upgrade_outbound(self, conn: TSubstream, _: Self::Info) -> Self::Future { let framed = Framed::new(conn, Codec::new()); RelayProxyRequestFuture { stream: framed, diff --git a/transports/relay/src/protocol/target_open.rs b/transports/relay/src/protocol/target_open.rs index c6fd71193c8..7c04c7c6067 100644 --- a/transports/relay/src/protocol/target_open.rs +++ b/transports/relay/src/protocol/target_open.rs @@ -64,12 +64,12 @@ impl RelayTargetOpen { } impl upgrade::UpgradeInfo for RelayTargetOpen { - type UpgradeId = (); - type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>; + type Info = &'static [u8]; + type InfoIter = iter::Once; #[inline] - fn protocol_names(&self) -> Self::NamesIter { - iter::once((Bytes::from("/libp2p/relay/circuit/0.1.0"), ())) + fn protocol_info(&self) -> Self::InfoIter { + iter::once("/libp2p/relay/circuit/0.1.0") } } @@ -82,7 +82,7 @@ where type Future = FutureResult; #[inline] - fn upgrade_outbound(self, conn: TSubstream, _: ()) -> Self::Future { + fn upgrade_outbound(self, conn: TSubstream, _: Self::Info) -> Self::Future { future::ok(conn) } }