From 638aa4c3f2fc358054df87741f3ad07f285d5b22 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sat, 5 Mar 2022 20:17:00 +0100 Subject: [PATCH] protocols/relay: Remove support for Circuit Relay v1 protocol --- protocols/relay/CHANGELOG.md | 2 + protocols/relay/build.rs | 1 - protocols/relay/examples/Dockerfile | 19 - protocols/relay/examples/docker-compose.yml | 63 - protocols/relay/examples/relay_v1.rs | 278 ---- protocols/relay/src/lib.rs | 1 - protocols/relay/src/v1.rs | 126 -- protocols/relay/src/v1/behaviour.rs | 801 ---------- protocols/relay/src/v1/connection.rs | 95 -- protocols/relay/src/v1/copy_future.rs | 137 -- protocols/relay/src/v1/handler.rs | 838 ---------- protocols/relay/src/v1/message.proto | 43 - protocols/relay/src/v1/protocol.rs | 105 -- .../relay/src/v1/protocol/incoming_dst_req.rs | 142 -- .../src/v1/protocol/incoming_relay_req.rs | 155 -- protocols/relay/src/v1/protocol/listen.rs | 178 --- .../relay/src/v1/protocol/outgoing_dst_req.rs | 220 --- .../src/v1/protocol/outgoing_relay_req.rs | 232 --- protocols/relay/src/v1/transport.rs | 584 ------- protocols/relay/tests/v1.rs | 1384 ----------------- 20 files changed, 2 insertions(+), 5402 deletions(-) delete mode 100644 protocols/relay/examples/Dockerfile delete mode 100644 protocols/relay/examples/docker-compose.yml delete mode 100644 protocols/relay/examples/relay_v1.rs delete mode 100644 protocols/relay/src/v1.rs delete mode 100644 protocols/relay/src/v1/behaviour.rs delete mode 100644 protocols/relay/src/v1/connection.rs delete mode 100644 protocols/relay/src/v1/copy_future.rs delete mode 100644 protocols/relay/src/v1/handler.rs delete mode 100644 protocols/relay/src/v1/message.proto delete mode 100644 protocols/relay/src/v1/protocol.rs delete mode 100644 protocols/relay/src/v1/protocol/incoming_dst_req.rs delete mode 100644 protocols/relay/src/v1/protocol/incoming_relay_req.rs delete mode 100644 protocols/relay/src/v1/protocol/listen.rs delete mode 100644 protocols/relay/src/v1/protocol/outgoing_dst_req.rs delete mode 100644 protocols/relay/src/v1/protocol/outgoing_relay_req.rs delete mode 100644 protocols/relay/src/v1/transport.rs delete mode 100644 protocols/relay/tests/v1.rs diff --git a/protocols/relay/CHANGELOG.md b/protocols/relay/CHANGELOG.md index 3cf5b0ae074..08130235c32 100644 --- a/protocols/relay/CHANGELOG.md +++ b/protocols/relay/CHANGELOG.md @@ -2,6 +2,8 @@ - Update to `libp2p-swarm` `v0.35.0`. +- Remove support for Circuit Relay v1 protocol. + # 0.7.0 [2022-02-22] - Update to `libp2p-core` `v0.32.0`. diff --git a/protocols/relay/build.rs b/protocols/relay/build.rs index a6776c9fab8..7859f6171f7 100644 --- a/protocols/relay/build.rs +++ b/protocols/relay/build.rs @@ -19,6 +19,5 @@ // DEALINGS IN THE SOFTWARE. fn main() { - prost_build::compile_protos(&["src/v1/message.proto"], &["src/v1"]).unwrap(); prost_build::compile_protos(&["src/v2/message.proto"], &["src/v2"]).unwrap(); } diff --git a/protocols/relay/examples/Dockerfile b/protocols/relay/examples/Dockerfile deleted file mode 100644 index cd511a879ce..00000000000 --- a/protocols/relay/examples/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# 1: Build the exe -FROM rust:1 as builder -LABEL Name=libp2p-relay Version=0.0.1 -# 1a: Prepare for static linking -RUN apt-get update && \ - apt-get dist-upgrade -y && \ - apt-get install -y musl-tools && \ - rustup target add x86_64-unknown-linux-musl -# 1b: Download and compile Rust dependencies (and store as a separate Docker layer) -WORKDIR /usr/src/libp2p - -COPY . . -RUN cargo build --target x86_64-unknown-linux-musl --example=relay --package=libp2p-relay - -# 2: Copy the exe and extra files ("static") to an empty Docker image -FROM debian:buster-slim - -COPY --from=builder /usr/src/libp2p/target/x86_64-unknown-linux-musl/debug/examples/relay . -USER 1000 \ No newline at end of file diff --git a/protocols/relay/examples/docker-compose.yml b/protocols/relay/examples/docker-compose.yml deleted file mode 100644 index 5ae38393465..00000000000 --- a/protocols/relay/examples/docker-compose.yml +++ /dev/null @@ -1,63 +0,0 @@ -# Run `docker-compose up` to start the setup. - -version: '2.1' -services: - relay: - image: libp2p-relay - command: - - "./relay" - - "--mode=relay" - - "--secret-key-seed=1" - - "--address=/ip6/::/tcp/4444" - build: - context: ../../../. - dockerfile: ./protocols/relay/examples/Dockerfile - networks: - - network-a - - network-b - - client-listen: - image: libp2p-relay - command: - - "./relay" - - "--mode=client-listen" - - "--secret-key-seed=2" - - "--address=/dns6/relay/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit" - build: - context: ../../../. - dockerfile: ./protocols/relay/examples/Dockerfile - depends_on: - - "relay" - networks: - - network-a - - client-dial: - image: libp2p-relay - command: - - "./relay" - - "--mode=client-dial" - - "--secret-key-seed=3" - - "--address=/dns6/relay/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3" - build: - context: ../../../. - dockerfile: ./protocols/relay/examples/Dockerfile - depends_on: - - "client-listen" - networks: - - network-b - -networks: - network-a: - driver: bridge - enable_ipv6: true - ipam: - driver: default - config: - - subnet: 2001:3984:3989::/64 - network-b: - driver: bridge - enable_ipv6: true - ipam: - driver: default - config: - - subnet: 2001:3984:3988::/64 diff --git a/protocols/relay/examples/relay_v1.rs b/protocols/relay/examples/relay_v1.rs deleted file mode 100644 index 30cb599c223..00000000000 --- a/protocols/relay/examples/relay_v1.rs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2020 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. - -//! A basic relay server and relay client implementation. -//! -//! The example below involves three nodes: (1) a relay server, (2) a listening -//! relay client listening via the relay server and (3) a dialing relay client -//! dialing the listening relay client via the relay server. -//! -//! 1. To start the relay server, run `cargo run --example=relay --package=libp2p-relay --mode relay --secret-key-seed 1 --address /ip4//tcp/`. -//! The `-secret-key-seed` helps create a static peer id using the given number argument as a seed. -//! The mode specifies whether the node should run as a relay server, a listening client or a dialing client. -//! The address specifies a static address. Usually it will be some loop back address such as `/ip4/0.0.0.0/tcp/4444`. -//! Example: -//! `cargo run --example=relay --package=libp2p-relay -- --mode relay --secret-key-seed 1 --address /ip4/0.0.0.0/tcp/4444` -//! `cargo run --example=relay --package=libp2p-relay -- --mode relay --secret-key-seed 1 --address /ip6/::/tcp/4444` -//! -//! 2. To start the listening relay client run `cargo run --example=relay --package=libp2p-relay -- --mode client-listen --secret-key-seed 2 --address -//! /p2p//p2p-circuit` in a second terminal where: -//! -//! - `` is replaced by one of the listening addresses of the relay server. -//! - `` is replaced by the peer id of the relay server. -//! -//! Example: -//! `cargo run --example=relay --package=libp2p-relay -- --mode client-listen --secret-key-seed 2 --address /ip4/127.0.0.1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit` -//! `cargo run --example=relay --package=libp2p-relay -- --mode client-listen --secret-key-seed 2 --address /ip6/::1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit` -//! -//! 3. To start the dialing relay client run `cargo run --example=relay --package=libp2p-relay -- --mode client-dial --secret-key-seed 3 --address -//! /p2p//p2p-circuit/p2p/` in -//! a third terminal where: -//! -//! - `` is replaced by one of the listening addresses of the relay server. -//! - `` is replaced by the peer id of the relay server. -//! - `` is replaced by the peer id of the listening relay client. -//! Example: -//! `cargo run --example=relay --package=libp2p-relay -- --mode client-dial --secret-key-seed 3 --address /ip4/127.0.0.1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3` -//! `cargo run --example=relay --package=libp2p-relay -- --mode client-dial --secret-key-seed 3 --address /ip6/::1/tcp/4444/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X/p2p-circuit/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3` -//! -//! In the third terminal you will see the dialing relay client to receive pings -//! from both the relay server AND from the listening relay client relayed via -//! the relay server. - -use futures::executor::block_on; -use futures::stream::StreamExt; -use libp2p::dns::DnsConfig; -use libp2p::plaintext; -use libp2p::relay::v1::{Relay, RelayConfig}; -use libp2p::swarm::SwarmEvent; -use libp2p::tcp::TcpConfig; -use libp2p::Transport; -use libp2p::{core::upgrade, identity::ed25519, ping, Multiaddr}; -use libp2p::{identity, NetworkBehaviour, PeerId, Swarm}; -use std::error::Error; -use std::task::{Context, Poll}; -use std::time::Duration; -use std::{fmt, str::FromStr}; -use structopt::StructOpt; - -// Listen on all interfaces and whatever port the OS assigns -const DEFAULT_RELAY_ADDRESS: &str = "/ip4/0.0.0.0/tcp/0"; - -fn main() -> Result<(), Box> { - env_logger::init(); - - let opt = Opt::from_args(); - println!("opt: {:?}", opt); - - // Create a static known PeerId based on given secret - let local_key: identity::Keypair = generate_ed25519(opt.secret_key_seed); - let local_peer_id = PeerId::from(local_key.public()); - println!("Local peer id: {:?}", local_peer_id); - - let transport = block_on(DnsConfig::system(TcpConfig::new()))?; - - let relay_config = RelayConfig { - connection_idle_timeout: Duration::from_secs(10 * 60), - ..Default::default() - }; - let (relay_wrapped_transport, relay_behaviour) = - libp2p_relay::v1::new_transport_and_behaviour(relay_config, transport); - - let behaviour = Behaviour { - relay: relay_behaviour, - ping: ping::Behaviour::new( - ping::Config::new() - .with_keep_alive(true) - .with_interval(Duration::from_secs(1)), - ), - }; - - let plaintext = plaintext::PlainText2Config { - local_public_key: local_key.public(), - }; - - let transport = relay_wrapped_transport - .upgrade(upgrade::Version::V1) - .authenticate(plaintext) - .multiplex(libp2p_yamux::YamuxConfig::default()) - .boxed(); - - let mut swarm = Swarm::new(transport, behaviour, local_peer_id); - - match opt.mode { - Mode::Relay => { - let address = get_relay_address(&opt); - swarm.listen_on(address.parse()?)?; - println!("starting listening as relay on {}", &address); - } - Mode::ClientListen => { - let relay_address = get_relay_peer_address(&opt); - swarm.listen_on(relay_address.parse()?)?; - println!("starting client listener via relay on {}", &relay_address); - } - Mode::ClientDial => { - let client_listen_address: Multiaddr = get_client_listen_address(&opt).parse()?; - swarm.dial(client_listen_address.clone())?; - println!("starting as client dialer on {}", client_listen_address); - } - } - - block_on(futures::future::poll_fn(move |cx: &mut Context<'_>| { - loop { - match swarm.poll_next_unpin(cx) { - Poll::Ready(Some(event)) => match event { - SwarmEvent::NewListenAddr { address, .. } => { - print_listener_peer(&address, &opt.mode, local_peer_id) - } - _ => println!("{:?}", event), - }, - Poll::Ready(None) => return Poll::Ready(Ok(())), - Poll::Pending => break, - } - } - Poll::Pending - })) -} - -fn print_listener_peer(addr: &libp2p::Multiaddr, mode: &Mode, local_peer_id: PeerId) -> () { - match mode { - Mode::Relay => { - println!( - "Peer that act as Relay can access on: `{}/p2p/{}/p2p-circuit`", - addr, local_peer_id - ); - } - Mode::ClientListen => { - println!( - "Peer that act as Client Listen can access on: `/p2p/{}/{}`", - addr, local_peer_id - ); - } - Mode::ClientDial => { - println!("Peer that act as Client Dial Listening on {:?}", addr); - } - } -} - -fn generate_ed25519(secret_key_seed: u8) -> identity::Keypair { - let mut bytes = [0u8; 32]; - bytes[0] = secret_key_seed; - - let secret_key = ed25519::SecretKey::from_bytes(&mut bytes) - .expect("this returns `Err` only if the length is wrong; the length is correct; qed"); - identity::Keypair::Ed25519(secret_key.into()) -} - -/// Get the address for relay mode -fn get_relay_address(opt: &Opt) -> String { - match &opt.address { - Some(address) => address.clone(), - None => { - println!("--address argument was not provided, will use the default listening relay address: {}",DEFAULT_RELAY_ADDRESS); - DEFAULT_RELAY_ADDRESS.to_string() - } - } -} - -/// Get the address for client_listen mode -fn get_relay_peer_address(opt: &Opt) -> String { - match &opt.address { - Some(address) => address.clone(), - None => panic!("Please provide relayed listen address such as: /p2p//p2p-circuit"), - } -} - -/// Get the address for client-dial mode -fn get_client_listen_address(opt: &Opt) -> String { - match &opt.address { - Some(address) => address.clone(), - None => panic!("Please provide client listen address such as: /p2p//p2p-circuit/p2p/") - } -} - -#[derive(NetworkBehaviour)] -#[behaviour(out_event = "Event")] -struct Behaviour { - relay: Relay, - ping: ping::Behaviour, -} - -#[derive(Debug)] -enum Event { - Relay(()), - Ping(ping::Event), -} - -impl From for Event { - fn from(v: ping::Event) -> Self { - Self::Ping(v) - } -} - -impl From<()> for Event { - fn from(_: ()) -> Self { - Event::Relay(()) - } -} - -#[derive(Debug, StructOpt)] -enum Mode { - Relay, - ClientListen, - ClientDial, -} - -impl FromStr for Mode { - type Err = ModeError; - fn from_str(mode: &str) -> Result { - match mode { - "relay" => Ok(Mode::Relay), - "client-listen" => Ok(Mode::ClientListen), - "client-dial" => Ok(Mode::ClientDial), - _ => Err(ModeError {}), - } - } -} - -#[derive(Debug)] -struct ModeError {} -impl Error for ModeError {} -impl fmt::Display for ModeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Could not parse a mode") - } -} - -#[derive(Debug, StructOpt)] -#[structopt(name = "libp2p relay")] -struct Opt { - /// The mode (relay, client-listen, client-dial) - #[structopt(long)] - mode: Mode, - - /// Fixed value to generate deterministic peer id - #[structopt(long)] - secret_key_seed: u8, - - /// The listening address - #[structopt(long)] - address: Option, -} diff --git a/protocols/relay/src/lib.rs b/protocols/relay/src/lib.rs index 02cfefbe639..79329335079 100644 --- a/protocols/relay/src/lib.rs +++ b/protocols/relay/src/lib.rs @@ -21,7 +21,6 @@ //! libp2p circuit relay implementations -pub mod v1; pub mod v2; // Check that we can safely cast a `usize` to a `u64`. diff --git a/protocols/relay/src/v1.rs b/protocols/relay/src/v1.rs deleted file mode 100644 index 83b7a7730d3..00000000000 --- a/protocols/relay/src/v1.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//! Implementation of the [libp2p circuit relay v1 -//! specification](https://github.com/libp2p/specs/tree/master/relay). -//! -//! ## Example -//! -//! ```rust -//! # use libp2p_core::transport::memory::MemoryTransport; -//! # use libp2p_relay::v1::{RelayConfig, new_transport_and_behaviour}; -//! # use libp2p_swarm::{Swarm, dial_opts::DialOpts}; -//! # use libp2p_core::{identity, Multiaddr, multiaddr::Protocol, PeerId, upgrade, Transport}; -//! # use libp2p_yamux::YamuxConfig; -//! # use libp2p_plaintext::PlainText2Config; -//! # use std::convert::TryInto; -//! # use std::str::FromStr; -//! # -//! # let local_key = identity::Keypair::generate_ed25519(); -//! # let local_public_key = local_key.public(); -//! # let local_peer_id = local_public_key.to_peer_id(); -//! # let plaintext = PlainText2Config { -//! # local_public_key: local_public_key.clone(), -//! # }; -//! # -//! let (relay_transport, relay_behaviour) = new_transport_and_behaviour( -//! RelayConfig::default(), -//! MemoryTransport::default(), -//! ); -//! -//! let transport = relay_transport -//! .upgrade(upgrade::Version::V1) -//! .authenticate(plaintext) -//! .multiplex(YamuxConfig::default()) -//! .boxed(); -//! -//! let mut swarm = Swarm::new(transport, relay_behaviour, local_peer_id); -//! -//! let relay_addr = Multiaddr::from_str("/memory/1234").unwrap() -//! .with(Protocol::P2p(PeerId::random().into())) -//! .with(Protocol::P2pCircuit); -//! let dst_addr = relay_addr.clone().with(Protocol::Memory(5678)); -//! -//! // Listen for incoming connections via relay node (1234). -//! swarm.listen_on(relay_addr).unwrap(); -//! -//! // Dial node (5678) via relay node (1234). -//! swarm.dial(dst_addr).unwrap(); -//! ``` -//! -//! ## Terminology -//! -//! ### Entities -//! -//! - **Source**: The node initiating a connection via a *relay* to a *destination*. -//! -//! - **Relay**: The node being asked by a *source* to relay to a *destination*. -//! -//! - **Destination**: The node contacted by the *source* via the *relay*. -//! -//! ### Messages -//! -//! - **Outgoing relay request**: The request sent by a *source* to a *relay*. -//! -//! - **Incoming relay request**: The request received by a *relay* from a *source*. -//! -//! - **Outgoing destination request**: The request sent by a *relay* to a *destination*. -//! -//! - **Incoming destination request**: The request received by a *destination* from a *relay*. - -mod behaviour; -mod connection; -mod copy_future; -mod handler; -mod protocol; -mod transport; - -pub use behaviour::{Relay, RelayConfig}; -pub use connection::Connection; -pub use transport::{RelayError, RelayListener, RelayTransport}; - -use libp2p_core::Transport; - -mod message_proto { - include!(concat!(env!("OUT_DIR"), "/message_v1.pb.rs")); -} - -/// Create both a [`RelayTransport`] wrapping the provided [`Transport`] -/// as well as a [`Relay`] [`NetworkBehaviour`](libp2p_swarm::NetworkBehaviour). -/// -/// Interconnects the returned [`RelayTransport`] and [`Relay`]. -pub fn new_transport_and_behaviour( - config: RelayConfig, - transport: T, -) -> (RelayTransport, Relay) { - let (transport, from_transport) = RelayTransport::new(transport); - let behaviour = Relay::new(config, from_transport); - (transport, behaviour) -} - -/// The ID of an outgoing / incoming, relay / destination request. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct RequestId(u64); - -impl RequestId { - fn new() -> RequestId { - RequestId(rand::random()) - } -} diff --git a/protocols/relay/src/v1/behaviour.rs b/protocols/relay/src/v1/behaviour.rs deleted file mode 100644 index 57e5b080864..00000000000 --- a/protocols/relay/src/v1/behaviour.rs +++ /dev/null @@ -1,801 +0,0 @@ -// Copyright 2019 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::v1::handler::{ - RelayHandlerConfig, RelayHandlerEvent, RelayHandlerIn, RelayHandlerProto, -}; -use crate::v1::message_proto::circuit_relay; -use crate::v1::transport::{OutgoingRelayReqError, TransportToBehaviourMsg}; -use crate::v1::{protocol, Connection, RequestId}; -use futures::channel::{mpsc, oneshot}; -use futures::prelude::*; -use libp2p_core::connection::{ConnectedPoint, ConnectionId, ListenerId}; -use libp2p_core::multiaddr::Multiaddr; -use libp2p_core::PeerId; -use libp2p_swarm::{ - dial_opts::{self, DialOpts}, - DialError, IntoConnectionHandler, NetworkBehaviour, NetworkBehaviourAction, NotifyHandler, - PollParameters, -}; -use std::collections::{hash_map::Entry, HashMap, HashSet, VecDeque}; -use std::task::{Context, Poll}; -use std::time::Duration; - -/// Network behaviour allowing the local node to act as a source, a relay and a destination. -pub struct Relay { - config: RelayConfig, - /// Channel receiver from [`crate::v1::RelayTransport`]. - from_transport: mpsc::Receiver, - - /// Events that need to be send to a [`RelayListener`](crate::v1::RelayListener) via - /// [`Self::listeners`] or [`Self::listener_any_relay`]. - outbox_to_listeners: VecDeque<(PeerId, BehaviourToListenerMsg)>, - /// Events that need to be yielded to the outside when polling. - outbox_to_swarm: VecDeque>, - - /// List of peers the network is connected to. - connected_peers: HashMap>, - - /// Requests by the local node to a relay to relay a connection for the local node to a - /// destination. - outgoing_relay_reqs: OutgoingRelayReqs, - - /// Requests for the local node to act as a relay from a source to a destination indexed by - /// destination [`PeerId`]. - incoming_relay_reqs: HashMap>, - - /// List of relay nodes via which the local node is explicitly listening for incoming relayed - /// connections. - /// - /// Indexed by relay [`PeerId`]. Contains channel sender to listener. - listeners: HashMap, - - /// Channel sender to listener listening for incoming relayed connections from relay nodes via - /// which the local node is not explicitly listening. - listener_any_relay: Option>, -} - -#[derive(Default)] -struct OutgoingRelayReqs { - /// Indexed by relay peer id. - dialing: HashMap>, - upgrading: HashMap, -} - -struct OutgoingDialingRelayReq { - request_id: RequestId, - src_peer_id: PeerId, - relay_addr: Multiaddr, - dst_addr: Option, - dst_peer_id: PeerId, - send_back: oneshot::Sender>, -} - -struct OutgoingUpgradingRelayReq { - send_back: oneshot::Sender>, -} - -enum IncomingRelayReq { - DialingDst { - src_peer_id: PeerId, - src_addr: Multiaddr, - src_connection_id: ConnectionId, - request_id: RequestId, - incoming_relay_req: protocol::IncomingRelayReq, - }, -} - -#[derive(Debug, Clone)] -pub struct RelayConfig { - /// How long to keep connections alive when they're idle. - /// - /// For a server, acting as a relay, allowing other nodes to listen for - /// incoming connections via oneself, this should likely be increased in - /// order not to force the peer to reconnect too regularly. - pub connection_idle_timeout: Duration, - /// Whether to actively establish an outgoing connection to a destination - /// node, when being asked by a source node to relay a connection to said - /// destination node. - /// - /// For security reasons this behaviour is disabled by default. Instead a - /// destination node should establish a connection to a relay node before - /// advertising their relayed address via that relay node to a source node. - pub actively_connect_to_dst_nodes: bool, -} - -impl Default for RelayConfig { - fn default() -> Self { - RelayConfig { - connection_idle_timeout: Duration::from_secs(10), - actively_connect_to_dst_nodes: false, - } - } -} - -// TODO: For now one is only able to specify relay servers via -// `Swarm::listen_on(Multiaddress(/p2p-circuit/))`. In the future -// we might want to support adding them via the Relay behaviour? The latter -// would allow other behaviours to manage ones relay listeners. -impl Relay { - /// Builds a new [`Relay`] [`NetworkBehaviour`]. - pub(crate) fn new( - config: RelayConfig, - from_transport: mpsc::Receiver, - ) -> Self { - Relay { - config, - from_transport, - outbox_to_listeners: Default::default(), - outbox_to_swarm: Default::default(), - connected_peers: Default::default(), - incoming_relay_reqs: Default::default(), - outgoing_relay_reqs: Default::default(), - listeners: Default::default(), - listener_any_relay: Default::default(), - } - } -} - -impl NetworkBehaviour for Relay { - type ConnectionHandler = RelayHandlerProto; - type OutEvent = (); - - fn new_handler(&mut self) -> Self::ConnectionHandler { - RelayHandlerProto { - config: RelayHandlerConfig { - connection_idle_timeout: self.config.connection_idle_timeout, - }, - } - } - - fn addresses_of_peer(&mut self, remote_peer_id: &PeerId) -> Vec { - self.listeners - .iter() - .filter_map(|(peer_id, r)| { - if let RelayListener::Connecting { relay_addr, .. } = r { - if peer_id == remote_peer_id { - return Some(relay_addr.clone()); - } - } - None - }) - .chain( - self.outgoing_relay_reqs - .dialing - .get(remote_peer_id) - .into_iter() - .flatten() - .map(|OutgoingDialingRelayReq { relay_addr, .. }| relay_addr.clone()), - ) - .chain( - self.incoming_relay_reqs - .get(remote_peer_id) - .into_iter() - .flatten() - .flat_map( - |IncomingRelayReq::DialingDst { - incoming_relay_req, .. - }| incoming_relay_req.dst_peer().addrs.clone(), - ), - ) - .collect() - } - - fn inject_connection_established( - &mut self, - peer: &PeerId, - connection_id: &ConnectionId, - _: &ConnectedPoint, - _: Option<&Vec>, - other_established: usize, - ) { - let is_first = self - .connected_peers - .entry(*peer) - .or_default() - .insert(*connection_id); - assert!( - is_first, - "`inject_connection_established` called with known connection id" - ); - - if let Some(RelayListener::Connecting { .. }) = self.listeners.get(peer) { - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: *peer, - handler: NotifyHandler::One(*connection_id), - event: RelayHandlerIn::UsedForListening(true), - }); - let mut to_listener = match self.listeners.remove(peer) { - None | Some(RelayListener::Connected { .. }) => unreachable!("See outer match."), - Some(RelayListener::Connecting { to_listener, .. }) => to_listener, - }; - to_listener - .start_send(BehaviourToListenerMsg::ConnectionToRelayEstablished) - .expect("Channel to have at least capacity of 1."); - self.listeners.insert( - *peer, - RelayListener::Connected { - connection_id: *connection_id, - to_listener, - }, - ); - } - - if other_established == 0 { - if let Some(reqs) = self.outgoing_relay_reqs.dialing.remove(peer) { - for req in reqs { - let OutgoingDialingRelayReq { - request_id, - src_peer_id, - relay_addr: _, - dst_addr, - dst_peer_id, - send_back, - } = req; - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: *peer, - handler: NotifyHandler::Any, - event: RelayHandlerIn::OutgoingRelayReq { - src_peer_id, - request_id, - dst_peer_id, - dst_addr: dst_addr.clone(), - }, - }); - - self.outgoing_relay_reqs - .upgrading - .insert(request_id, OutgoingUpgradingRelayReq { send_back }); - } - } - - // Ask the newly-opened connection to be used as destination if relevant. - if let Some(reqs) = self.incoming_relay_reqs.remove(peer) { - for req in reqs { - let IncomingRelayReq::DialingDst { - src_peer_id, - src_addr, - src_connection_id, - request_id, - incoming_relay_req, - } = req; - let event = RelayHandlerIn::OutgoingDstReq { - src_peer_id, - src_addr, - src_connection_id, - request_id, - incoming_relay_req, - }; - - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: *peer, - handler: NotifyHandler::Any, - event, - }); - } - } - } - } - - fn inject_dial_failure( - &mut self, - peer_id: Option, - _: Self::ConnectionHandler, - error: &DialError, - ) { - if let DialError::DialPeerConditionFalse( - dial_opts::PeerCondition::Disconnected | dial_opts::PeerCondition::NotDialing, - ) = error - { - // Return early. The dial, that this dial was canceled for, might still succeed. - return; - } - - if let Some(peer_id) = peer_id { - if let Entry::Occupied(o) = self.listeners.entry(peer_id) { - if matches!(o.get(), RelayListener::Connecting { .. }) { - // By removing the entry, the channel to the listener is dropped and thus the - // listener is notified that dialing the relay failed. - o.remove_entry(); - } - } - - if let Some(reqs) = self.outgoing_relay_reqs.dialing.remove(&peer_id) { - for req in reqs { - let _ = req.send_back.send(Err(OutgoingRelayReqError::DialingRelay)); - } - } - - if let Some(reqs) = self.incoming_relay_reqs.remove(&peer_id) { - for req in reqs { - let IncomingRelayReq::DialingDst { - src_peer_id, - incoming_relay_req, - .. - } = req; - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: src_peer_id, - handler: NotifyHandler::Any, - event: RelayHandlerIn::DenyIncomingRelayReq( - incoming_relay_req.deny(circuit_relay::Status::HopCantDialDst), - ), - }) - } - } - } - } - - fn inject_connection_closed( - &mut self, - peer: &PeerId, - connection: &ConnectionId, - _: &ConnectedPoint, - _: ::Handler, - remaining_established: usize, - ) { - // Remove connection from the set of connections for the given peer. In case the set is - // empty it will be removed in `inject_disconnected`. - let was_present = self - .connected_peers - .get_mut(peer) - .expect("`inject_connection_closed` called for connected peer.") - .remove(connection); - assert!( - was_present, - "`inject_connection_closed` called for known connection" - ); - - match self.listeners.get(peer) { - None => {} - Some(RelayListener::Connecting { .. }) => unreachable!( - "State mismatch. Listener waiting for connection while \ - connection previously established.", - ), - Some(RelayListener::Connected { connection_id, .. }) => { - if connection_id == connection { - if let Some(new_primary) = self - .connected_peers - .get(peer) - .and_then(|cs| cs.iter().next()) - { - let to_listener = match self.listeners.remove(peer) { - None | Some(RelayListener::Connecting { .. }) => { - unreachable!("Due to outer match.") - } - Some(RelayListener::Connected { to_listener, .. }) => to_listener, - }; - self.listeners.insert( - *peer, - RelayListener::Connected { - connection_id: *new_primary, - to_listener, - }, - ); - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: *peer, - handler: NotifyHandler::One(*new_primary), - event: RelayHandlerIn::UsedForListening(true), - }); - } else { - // There are no more connections to the relay left that - // could be promoted as primary. Remove the listener, - // notifying the listener by dropping the channel to it. - self.listeners.remove(peer); - } - } - } - } - - if remaining_established == 0 { - self.connected_peers.remove(peer); - - if let Some(reqs) = self.incoming_relay_reqs.remove(peer) { - for req in reqs { - let IncomingRelayReq::DialingDst { - src_peer_id, - incoming_relay_req, - .. - } = req; - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: src_peer_id, - handler: NotifyHandler::Any, - event: RelayHandlerIn::DenyIncomingRelayReq( - incoming_relay_req.deny(circuit_relay::Status::HopCantDialDst), - ), - }) - } - } - } - } - - fn inject_listener_error(&mut self, _id: ListenerId, _err: &(dyn std::error::Error + 'static)) { - } - - fn inject_listener_closed(&mut self, _id: ListenerId, _reason: Result<(), &std::io::Error>) {} - - fn inject_event( - &mut self, - event_source: PeerId, - connection: ConnectionId, - event: RelayHandlerEvent, - ) { - match event { - // Remote wants us to become a relay. - RelayHandlerEvent::IncomingRelayReq { - request_id, - src_addr, - req, - } => { - if self.connected_peers.get(&req.dst_peer().peer_id).is_some() { - let dest_id = req.dst_peer().peer_id; - let event = RelayHandlerIn::OutgoingDstReq { - src_peer_id: event_source, - src_addr, - src_connection_id: connection, - request_id, - incoming_relay_req: req, - }; - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: dest_id, - handler: NotifyHandler::Any, - event, - }); - } else { - if self.config.actively_connect_to_dst_nodes { - let dest_id = req.dst_peer().peer_id; - self.incoming_relay_reqs.entry(dest_id).or_default().push( - IncomingRelayReq::DialingDst { - request_id, - incoming_relay_req: req, - src_peer_id: event_source, - src_addr, - src_connection_id: connection, - }, - ); - let handler = self.new_handler(); - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::Dial { - opts: DialOpts::peer_id(dest_id) - .condition(dial_opts::PeerCondition::NotDialing) - .build(), - handler, - }); - } else { - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: event_source, - handler: NotifyHandler::One(connection), - event: RelayHandlerIn::DenyIncomingRelayReq( - req.deny(circuit_relay::Status::HopNoConnToDst), - ), - }); - } - } - } - // Remote wants us to become a destination. - RelayHandlerEvent::IncomingDstReq(request) => { - let got_explicit_listener = self - .listeners - .get(&event_source) - .map(|l| !l.is_closed()) - .unwrap_or(false); - let got_listener_for_any_relay = self - .listener_any_relay - .as_mut() - .map(|l| !l.is_closed()) - .unwrap_or(false); - - let send_back = if got_explicit_listener || got_listener_for_any_relay { - RelayHandlerIn::AcceptDstReq(request) - } else { - RelayHandlerIn::DenyDstReq(request) - }; - - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: event_source, - handler: NotifyHandler::One(connection), - event: send_back, - }); - } - RelayHandlerEvent::OutgoingRelayReqError(_dst_peer_id, request_id) => { - self.outgoing_relay_reqs - .upgrading - .remove(&request_id) - .expect("Outgoing relay request error for unknown request."); - } - RelayHandlerEvent::OutgoingRelayReqSuccess(_dst, request_id, stream) => { - let send_back = self - .outgoing_relay_reqs - .upgrading - .remove(&request_id) - .map(|OutgoingUpgradingRelayReq { send_back, .. }| send_back) - .expect("Outgoing relay request success for unknown request."); - let _ = send_back.send(Ok(stream)); - } - RelayHandlerEvent::IncomingDstReqSuccess { - stream, - src_peer_id, - relay_peer_id, - relay_addr, - } => self.outbox_to_listeners.push_back(( - relay_peer_id, - BehaviourToListenerMsg::IncomingRelayedConnection { - stream, - src_peer_id, - relay_peer_id, - relay_addr, - }, - )), - RelayHandlerEvent::OutgoingDstReqError { - src_connection_id, - incoming_relay_req_deny_fut, - } => { - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: event_source, - handler: NotifyHandler::One(src_connection_id), - event: RelayHandlerIn::DenyIncomingRelayReq(incoming_relay_req_deny_fut), - }); - } - } - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - poll_parameters: &mut impl PollParameters, - ) -> Poll> { - if !self.outbox_to_listeners.is_empty() { - let relay_peer_id = self.outbox_to_listeners[0].0; - - let listeners = &mut self.listeners; - let listener_any_relay = self.listener_any_relay.as_mut(); - - // Get channel sender to the listener that is explicitly listening - // via this relay node, or, if registered, channel sender to - // listener listening via any relay. - let to_listener = listeners - .get_mut(&relay_peer_id) - .filter(|l| !l.is_closed()) - .and_then(|l| match l { - RelayListener::Connected { to_listener, .. } => Some(to_listener), - // State mismatch. Got relayed connection via relay, but - // local node is not connected to relay. - RelayListener::Connecting { .. } => None, - }) - .or_else(|| listener_any_relay) - .filter(|l| !l.is_closed()); - - match to_listener { - Some(to_listener) => match to_listener.poll_ready(cx) { - Poll::Ready(Ok(())) => { - if let Err(mpsc::SendError { .. }) = to_listener.start_send( - self.outbox_to_listeners - .pop_front() - .expect("Outbox is empty despite !is_empty().") - .1, - ) { - self.listeners.remove(&relay_peer_id); - } - } - Poll::Ready(Err(mpsc::SendError { .. })) => { - self.outbox_to_listeners.pop_front(); - self.listeners.remove(&relay_peer_id); - } - Poll::Pending => {} - }, - None => { - // No listener to send request to, thus dropping it. This - // case should be rare, as we check whether we have a - // listener before accepting an incoming destination - // request. - let event = self.outbox_to_listeners.pop_front(); - log::trace!("Dropping event for unknown listener: {:?}", event); - } - } - } - - loop { - match self.from_transport.poll_next_unpin(cx) { - Poll::Ready(Some(TransportToBehaviourMsg::DialReq { - request_id, - relay_addr, - relay_peer_id, - dst_addr, - dst_peer_id, - send_back, - })) => { - if self.connected_peers.get(&relay_peer_id).is_some() { - // In case we are already listening via the relay, - // prefer the primary connection. - let handler = self - .listeners - .get(&relay_peer_id) - .and_then(|s| { - if let RelayListener::Connected { connection_id, .. } = s { - Some(NotifyHandler::One(*connection_id)) - } else { - None - } - }) - .unwrap_or(NotifyHandler::Any); - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: relay_peer_id, - handler, - event: RelayHandlerIn::OutgoingRelayReq { - request_id, - src_peer_id: *poll_parameters.local_peer_id(), - dst_peer_id, - dst_addr: dst_addr.clone(), - }, - }); - - self.outgoing_relay_reqs - .upgrading - .insert(request_id, OutgoingUpgradingRelayReq { send_back }); - } else { - self.outgoing_relay_reqs - .dialing - .entry(relay_peer_id) - .or_default() - .push(OutgoingDialingRelayReq { - src_peer_id: *poll_parameters.local_peer_id(), - request_id, - relay_addr, - dst_addr, - dst_peer_id, - send_back, - }); - return Poll::Ready(NetworkBehaviourAction::Dial { - opts: DialOpts::peer_id(relay_peer_id) - .condition(dial_opts::PeerCondition::Disconnected) - .build(), - handler: self.new_handler(), - }); - } - } - Poll::Ready(Some(TransportToBehaviourMsg::ListenReq { - relay_peer_id_and_addr, - mut to_listener, - })) => { - match relay_peer_id_and_addr { - // Listener is listening for all incoming relayed - // connections from any relay - // node. - None => { - match self.listener_any_relay.as_mut() { - Some(sender) if !sender.is_closed() => { - // Already got listener listening for all - // incoming relayed connections. Signal to - // listener by dropping the channel sender - // to the listener. - } - _ => { - to_listener - .start_send( - BehaviourToListenerMsg::ConnectionToRelayEstablished, - ) - .expect("Channel to have at least capacity of 1."); - self.listener_any_relay = Some(to_listener); - } - } - } - // Listener is listening for incoming relayed - // connections from this relay only. - Some((relay_peer_id, relay_addr)) => { - if let Some(connections) = self.connected_peers.get(&relay_peer_id) { - to_listener - .start_send( - BehaviourToListenerMsg::ConnectionToRelayEstablished, - ) - .expect("Channel to have at least capacity of 1."); - let primary_connection = - connections.iter().next().expect("At least one connection."); - self.listeners.insert( - relay_peer_id, - RelayListener::Connected { - connection_id: *primary_connection, - to_listener, - }, - ); - - self.outbox_to_swarm.push_back( - NetworkBehaviourAction::NotifyHandler { - peer_id: relay_peer_id, - handler: NotifyHandler::One(*primary_connection), - event: RelayHandlerIn::UsedForListening(true), - }, - ); - } else { - self.listeners.insert( - relay_peer_id, - RelayListener::Connecting { - relay_addr, - to_listener, - }, - ); - return Poll::Ready(NetworkBehaviourAction::Dial { - opts: DialOpts::peer_id(relay_peer_id) - .condition(dial_opts::PeerCondition::Disconnected) - .build(), - handler: self.new_handler(), - }); - } - } - } - } - Poll::Ready(None) => unreachable!( - "`Relay` `NetworkBehaviour` polled after channel from \ - `RelayTransport` has been closed.", - ), - Poll::Pending => break, - } - } - - if let Some(event) = self.outbox_to_swarm.pop_front() { - return Poll::Ready(event); - } - - Poll::Pending - } -} - -enum RelayListener { - Connecting { - relay_addr: Multiaddr, - to_listener: mpsc::Sender, - }, - Connected { - connection_id: ConnectionId, - to_listener: mpsc::Sender, - }, -} - -impl RelayListener { - /// Returns whether the channel to the - /// [`RelayListener`](crate::v1::RelayListener) is closed. - fn is_closed(&self) -> bool { - match self { - RelayListener::Connecting { to_listener, .. } - | RelayListener::Connected { to_listener, .. } => to_listener.is_closed(), - } - } -} - -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum BehaviourToListenerMsg { - ConnectionToRelayEstablished, - IncomingRelayedConnection { - stream: Connection, - src_peer_id: PeerId, - relay_peer_id: PeerId, - relay_addr: Multiaddr, - }, -} diff --git a/protocols/relay/src/v1/connection.rs b/protocols/relay/src/v1/connection.rs deleted file mode 100644 index 99b12d04a42..00000000000 --- a/protocols/relay/src/v1/connection.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 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 futures::channel::oneshot; -use futures::io::{AsyncRead, AsyncWrite}; -use libp2p_swarm::NegotiatedSubstream; -use std::io::{Error, IoSlice}; -use std::pin::Pin; -use std::task::{Context, Poll}; - -/// A [`NegotiatedSubstream`] acting as a relayed [`Connection`]. -#[derive(Debug)] -pub struct Connection { - /// [`Connection`] might at first return data, that was already read during relay negotiation. - initial_data: Bytes, - stream: NegotiatedSubstream, - /// Notifies the other side of the channel of this [`Connection`] being dropped. - _notifier: oneshot::Sender<()>, -} - -impl Unpin for Connection {} - -impl Connection { - pub fn new( - initial_data: Bytes, - stream: NegotiatedSubstream, - notifier: oneshot::Sender<()>, - ) -> Self { - Connection { - initial_data, - stream, - - _notifier: notifier, - } - } -} - -impl AsyncWrite for Connection { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut self.stream).poll_write(cx, buf) - } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - Pin::new(&mut self.stream).poll_flush(cx) - } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - Pin::new(&mut self.stream).poll_close(cx) - } - - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut Context, - bufs: &[IoSlice], - ) -> Poll> { - Pin::new(&mut self.stream).poll_write_vectored(cx, bufs) - } -} - -impl AsyncRead for Connection { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - if !self.initial_data.is_empty() { - let n = std::cmp::min(self.initial_data.len(), buf.len()); - let data = self.initial_data.split_to(n); - buf[0..n].copy_from_slice(&data[..]); - return Poll::Ready(Ok(n)); - } - - Pin::new(&mut self.stream).poll_read(cx, buf) - } -} diff --git a/protocols/relay/src/v1/copy_future.rs b/protocols/relay/src/v1/copy_future.rs deleted file mode 100644 index 5b6b9a50c82..00000000000 --- a/protocols/relay/src/v1/copy_future.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2020 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. - -//! Helper to interconnect two substreams, connecting the receiver side of A with the sender side of -//! B and vice versa. -//! -//! Inspired by [`futures::io::Copy`]. - -use futures::future::Future; -use futures::future::FutureExt; -use futures::io::{AsyncBufRead, BufReader}; -use futures::io::{AsyncRead, AsyncWrite}; -use futures::ready; -use futures_timer::Delay; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::time::Duration; - -pub struct CopyFuture { - src: BufReader, - dst: BufReader, - - active_timeout: Delay, - configured_timeout: Duration, -} - -impl CopyFuture { - pub fn new(src: S, dst: D, timeout: Duration) -> Self { - CopyFuture { - src: BufReader::new(src), - dst: BufReader::new(dst), - active_timeout: Delay::new(timeout), - configured_timeout: timeout, - } - } -} - -impl Future for CopyFuture -where - S: AsyncRead + AsyncWrite + Unpin, - D: AsyncRead + AsyncWrite + Unpin, -{ - type Output = io::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = &mut *self; - - let mut reset_timer = false; - - loop { - enum Status { - Pending, - Done, - Progressed, - } - - let src_status = match forward_data(&mut this.src, &mut this.dst, cx) { - Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), - Poll::Ready(Ok(true)) => Status::Done, - Poll::Ready(Ok(false)) => Status::Progressed, - Poll::Pending => Status::Pending, - }; - - let dst_status = match forward_data(&mut this.dst, &mut this.src, cx) { - Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), - Poll::Ready(Ok(true)) => Status::Done, - Poll::Ready(Ok(false)) => Status::Progressed, - Poll::Pending => Status::Pending, - }; - - match (src_status, dst_status) { - // Both source and destination are done sending data. - (Status::Done, Status::Done) => return Poll::Ready(Ok(())), - // Either source or destination made progress, thus reset timer. - (Status::Progressed, _) | (_, Status::Progressed) => reset_timer = true, - // Both are pending. Check if timer fired, otherwise return Poll::Pending. - (Status::Pending, Status::Pending) => break, - // One is done sending data, the other is pending. Check if timer fired, otherwise - // return Poll::Pending. - (Status::Pending, Status::Done) | (Status::Done, Status::Pending) => break, - } - } - - if reset_timer { - this.active_timeout = Delay::new(this.configured_timeout); - } - - if let Poll::Ready(()) = this.active_timeout.poll_unpin(cx) { - return Poll::Ready(Err(io::ErrorKind::TimedOut.into())); - } - - Poll::Pending - } -} - -/// Forwards data from `source` to `destination`. -/// -/// Returns `true` when done, i.e. `source` having reached EOF, returns false otherwise, thus -/// indicating progress. -fn forward_data( - mut src: &mut S, - mut dst: &mut D, - cx: &mut Context<'_>, -) -> Poll> { - let buffer = ready!(Pin::new(&mut src).poll_fill_buf(cx))?; - if buffer.is_empty() { - ready!(Pin::new(&mut dst).poll_flush(cx))?; - ready!(Pin::new(&mut dst).poll_close(cx))?; - return Poll::Ready(Ok(true)); - } - - let i = ready!(Pin::new(dst).poll_write(cx, buffer))?; - if i == 0 { - return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); - } - Pin::new(src).consume(i); - - Poll::Ready(Ok(false)) -} diff --git a/protocols/relay/src/v1/handler.rs b/protocols/relay/src/v1/handler.rs deleted file mode 100644 index 97bfff7cc8e..00000000000 --- a/protocols/relay/src/v1/handler.rs +++ /dev/null @@ -1,838 +0,0 @@ -// Copyright 2019 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::v1::message_proto::circuit_relay; -use crate::v1::{protocol, Connection, RequestId}; -use futures::channel::oneshot::{self, Canceled}; -use futures::future::BoxFuture; -use futures::prelude::*; -use futures::stream::FuturesUnordered; -use instant::Instant; -use libp2p_core::connection::ConnectionId; -use libp2p_core::either::{EitherError, EitherOutput}; -use libp2p_core::{upgrade, ConnectedPoint, Multiaddr, PeerId}; -use libp2p_swarm::{ - ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, IntoConnectionHandler, - KeepAlive, NegotiatedSubstream, SubstreamProtocol, -}; -use log::warn; -use std::fmt; -use std::task::{Context, Poll}; -use std::time::Duration; - -#[derive(Debug, Clone)] -pub struct RelayHandlerConfig { - pub connection_idle_timeout: Duration, -} - -pub struct RelayHandlerProto { - pub config: RelayHandlerConfig, -} - -impl IntoConnectionHandler for RelayHandlerProto { - type Handler = RelayHandler; - - fn into_handler(self, remote_peer_id: &PeerId, endpoint: &ConnectedPoint) -> Self::Handler { - RelayHandler::new( - self.config, - *remote_peer_id, - endpoint.get_remote_address().clone(), - ) - } - - fn inbound_protocol(&self) -> ::InboundProtocol { - protocol::RelayListen::new() - } -} - -/// Protocol handler that handles the relay protocol. -/// -/// There are four possible situations in play here: -/// -/// - The handler emits [`RelayHandlerEvent::IncomingRelayReq`] if the node we handle asks us to act -/// as a relay. You must send a [`RelayHandlerIn::OutgoingDstReq`] to another handler, or send -/// back a [`RelayHandlerIn::DenyIncomingRelayReq`]. -/// -/// - The handler emits [`RelayHandlerEvent::IncomingDstReq`] if the node we handle asks us to act -/// as a destination. You must either send back a [`RelayHandlerIn::AcceptDstReq`]`, or send back -/// a [`RelayHandlerIn::DenyDstReq`]. -/// -/// - Send a [`RelayHandlerIn::OutgoingRelayReq`] if the node we handle must act as a relay to a -/// destination. The handler will either send back a -/// [`RelayHandlerEvent::OutgoingRelayReqSuccess`] containing the stream to the destination, or a -/// [`RelayHandlerEvent::OutgoingRelayReqError`]. -/// -/// - Send a [`RelayHandlerIn::OutgoingDstReq`] if the node we handle must act as a destination. The -/// handler will automatically notify the source whether the request was accepted or denied. -pub struct RelayHandler { - config: RelayHandlerConfig, - /// Specifies whether the handled connection is used to listen for incoming relayed connections. - used_for_listening: bool, - remote_address: Multiaddr, - remote_peer_id: PeerId, - /// Futures that send back negative responses. - deny_futures: FuturesUnordered>>, - /// Futures that send back an accept response to a relay. - accept_dst_futures: FuturesUnordered< - BoxFuture< - 'static, - Result<(PeerId, Connection, oneshot::Receiver<()>), protocol::IncomingDstReqError>, - >, - >, - /// Futures that copy from a source to a destination. - copy_futures: FuturesUnordered>>, - /// Requests asking the remote to become a relay. - outgoing_relay_reqs: Vec, - /// Requests asking the remote to become a destination. - outgoing_dst_reqs: Vec, - /// Queue of events to return when polled. - queued_events: Vec, - /// Tracks substreams lend out to other [`RelayHandler`]s or as - /// [`Connection`](Connection) to the - /// [`RelayTransport`](crate::v1::RelayTransport). - /// - /// For each substream to the peer of this handler, there is a future in here that resolves once - /// the given substream is dropped. - /// - /// Once all substreams are dropped and this handler has no other work, [`KeepAlive::Until`] can - /// be set, allowing the connection to be closed eventually. - alive_lend_out_substreams: FuturesUnordered>, - /// The current connection keep-alive. - keep_alive: KeepAlive, - /// A pending fatal error that results in the connection being closed. - pending_error: Option< - ConnectionHandlerUpgrErr< - EitherError< - protocol::RelayListenError, - EitherError, - >, - >, - >, -} - -struct OutgoingRelayReq { - src_peer_id: PeerId, - dst_peer_id: PeerId, - request_id: RequestId, - /// Addresses of the destination. - dst_addr: Option, -} - -struct OutgoingDstReq { - src_peer_id: PeerId, - src_addr: Multiaddr, - src_connection_id: ConnectionId, - request_id: RequestId, - incoming_relay_req: protocol::IncomingRelayReq, -} - -/// Event produced by the relay handler. -pub enum RelayHandlerEvent { - /// The remote wants us to relay communications to a third party. You must either send back a - /// [`RelayHandlerIn::DenyIncomingRelayReq`], or an [`RelayHandlerIn::OutgoingDstReq`] to any - /// connection handler for the destination peer, providing the [`protocol::IncomingRelayReq`]. - IncomingRelayReq { - request_id: RequestId, - src_addr: Multiaddr, - req: protocol::IncomingRelayReq, - }, - - /// The remote is a relay and is relaying a connection to us. In other words, we are used as - /// a destination. The behaviour can accept or deny the request via - /// [`AcceptDstReq`](RelayHandlerIn::AcceptDstReq) or - /// [`DenyDstReq`](RelayHandlerIn::DenyDstReq). - IncomingDstReq(protocol::IncomingDstReq), - - /// A `RelayReq` that has previously been sent has been accepted by the remote. Contains - /// a substream that communicates with the requested destination. - /// - /// > **Note**: There is no proof that we are actually communicating with the destination. An - /// > encryption handshake has to be performed on top of this substream in order to - /// > avoid MITM attacks. - OutgoingRelayReqSuccess(PeerId, RequestId, Connection), - - /// The local node has accepted an incoming destination request. Contains a substream that - /// communicates with the source. - /// - /// > **Note**: There is no proof that we are actually communicating with the destination. An - /// > encryption handshake has to be performed on top of this substream in order to - /// > avoid MITM attacks. - IncomingDstReqSuccess { - stream: Connection, - src_peer_id: PeerId, - relay_peer_id: PeerId, - relay_addr: Multiaddr, - }, - - /// A `RelayReq` that has previously been sent by the local node has failed. - OutgoingRelayReqError(PeerId, RequestId), - - /// A destination request that has previously been sent by the local node has failed. - /// - /// Includes the incoming relay request, which is yet to be denied due to the failure. - OutgoingDstReqError { - src_connection_id: ConnectionId, - incoming_relay_req_deny_fut: BoxFuture<'static, Result<(), std::io::Error>>, - }, -} - -impl fmt::Debug for RelayHandlerEvent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RelayHandlerEvent::IncomingRelayReq { - request_id, - src_addr, - req: _, - } => f - .debug_struct("RelayHandlerEvent::IncomingRelayReq") - .field("request_id", request_id) - .field("src_addr", src_addr) - .finish(), - RelayHandlerEvent::IncomingDstReq(_) => { - f.debug_tuple("RelayHandlerEvent::IncomingDstReq").finish() - } - RelayHandlerEvent::OutgoingRelayReqSuccess(peer_id, request_id, connection) => f - .debug_tuple("RelayHandlerEvent::OutgoingRelayReqSuccess") - .field(peer_id) - .field(request_id) - .field(connection) - .finish(), - RelayHandlerEvent::IncomingDstReqSuccess { - stream, - src_peer_id, - relay_peer_id, - relay_addr, - } => f - .debug_struct("RelayHandlerEvent::IncomingDstReqSuccess") - .field("stream", stream) - .field("src_peer_id", src_peer_id) - .field("relay_peer_id", relay_peer_id) - .field("relay_addr", relay_addr) - .finish(), - RelayHandlerEvent::OutgoingRelayReqError(peer_id, request_id) => f - .debug_tuple("RelayHandlerEvent::OutgoingRelayReqError") - .field(peer_id) - .field(request_id) - .finish(), - RelayHandlerEvent::OutgoingDstReqError { - src_connection_id, - incoming_relay_req_deny_fut: _, - } => f - .debug_struct("RelayHandlerEvent::OutgoingDstReqError") - .field("src_connection_id", src_connection_id) - .finish(), - } - } -} - -/// Event that can be sent to the relay handler. -pub enum RelayHandlerIn { - /// Tell the handler whether it is handling a connection used to listen for incoming relayed - /// connections. - UsedForListening(bool), - /// Denies a relay request sent by the node we talk to acting as a source. - DenyIncomingRelayReq(BoxFuture<'static, Result<(), std::io::Error>>), - - /// Accepts a destination request sent by the node we talk to. - AcceptDstReq(protocol::IncomingDstReq), - - /// Denies a destination request sent by the node we talk to. - DenyDstReq(protocol::IncomingDstReq), - - /// Opens a new substream to the remote and asks it to relay communications to a third party. - OutgoingRelayReq { - src_peer_id: PeerId, - dst_peer_id: PeerId, - request_id: RequestId, - /// Addresses known for this peer to transmit to the remote. - dst_addr: Option, - }, - - /// Asks the node to be used as a destination for a relayed connection. - /// - /// The positive or negative response will be written to `substream`. - OutgoingDstReq { - /// Peer id of the node whose communications are being relayed. - src_peer_id: PeerId, - /// Address of the node whose communications are being relayed. - src_addr: Multiaddr, - src_connection_id: ConnectionId, - request_id: RequestId, - /// Incoming relay request from the source node. - incoming_relay_req: protocol::IncomingRelayReq, - }, -} - -impl fmt::Debug for RelayHandlerIn { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RelayHandlerIn::UsedForListening(_) => { - f.debug_tuple("RelayHandlerIn::UsedForListening").finish() - } - RelayHandlerIn::DenyIncomingRelayReq(_) => f - .debug_tuple("RelayHandlerIn::DenyIncomingRelayReq") - .finish(), - RelayHandlerIn::AcceptDstReq(_) => { - f.debug_tuple("RelayHandlerIn::AcceptDstReq").finish() - } - RelayHandlerIn::DenyDstReq(_) => f.debug_tuple("RelayHandlerIn::DenyDstReq").finish(), - RelayHandlerIn::OutgoingRelayReq { - src_peer_id, - dst_peer_id, - request_id, - dst_addr, - } => f - .debug_struct("RelayHandlerIn::OutgoingRelayReq") - .field("src_peer_id", src_peer_id) - .field("dst_peer_id", dst_peer_id) - .field("request_id", request_id) - .field("dst_addr", dst_addr) - .finish(), - RelayHandlerIn::OutgoingDstReq { - src_peer_id, - src_addr, - src_connection_id, - request_id, - incoming_relay_req: _, - } => f - .debug_struct("RelayHandlerIn::OutgoingDstReq") - .field("src_peer_id", src_peer_id) - .field("src_addr", src_addr) - .field("src_connection_id", src_connection_id) - .field("request_id", request_id) - .finish(), - } - } -} - -impl RelayHandler { - /// Builds a new `RelayHandler`. - pub fn new( - config: RelayHandlerConfig, - remote_peer_id: PeerId, - remote_address: Multiaddr, - ) -> Self { - RelayHandler { - config, - used_for_listening: false, - remote_address, - remote_peer_id, - deny_futures: Default::default(), - accept_dst_futures: Default::default(), - copy_futures: Default::default(), - outgoing_relay_reqs: Default::default(), - outgoing_dst_reqs: Default::default(), - queued_events: Default::default(), - alive_lend_out_substreams: Default::default(), - keep_alive: KeepAlive::Yes, - pending_error: None, - } - } -} - -impl ConnectionHandler for RelayHandler { - type InEvent = RelayHandlerIn; - type OutEvent = RelayHandlerEvent; - type Error = ConnectionHandlerUpgrErr< - EitherError< - protocol::RelayListenError, - EitherError, - >, - >; - type InboundProtocol = protocol::RelayListen; - type OutboundProtocol = - upgrade::EitherUpgrade; - type OutboundOpenInfo = RelayOutboundOpenInfo; - type InboundOpenInfo = RequestId; - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(protocol::RelayListen::new(), RequestId::new()) - } - - fn inject_fully_negotiated_inbound( - &mut self, - protocol: >::Output, - request_id: Self::InboundOpenInfo, - ) { - match protocol { - // We have been asked to act as a relay. - protocol::RelayRemoteReq::RelayReq((incoming_relay_request, notifyee)) => { - self.alive_lend_out_substreams.push(notifyee); - self.queued_events - .push(RelayHandlerEvent::IncomingRelayReq { - request_id, - src_addr: self.remote_address.clone(), - req: incoming_relay_request, - }); - } - // We have been asked to become a destination. - protocol::RelayRemoteReq::DstReq(dst_request) => { - self.queued_events - .push(RelayHandlerEvent::IncomingDstReq(dst_request)); - } - } - } - - fn inject_fully_negotiated_outbound( - &mut self, - protocol: >::Output, - open_info: Self::OutboundOpenInfo, - ) { - match protocol { - // We have successfully negotiated a substream towards a relay. - EitherOutput::First((substream_to_dest, notifyee)) => { - let (dst_peer_id, request_id) = match open_info { - RelayOutboundOpenInfo::Relay { - dst_peer_id, - request_id, - } => (dst_peer_id, request_id), - RelayOutboundOpenInfo::Destination { .. } => unreachable!( - "Can not successfully dial a relay when actually dialing a destination." - ), - }; - - self.alive_lend_out_substreams.push(notifyee); - self.queued_events - .push(RelayHandlerEvent::OutgoingRelayReqSuccess( - dst_peer_id, - request_id, - substream_to_dest, - )); - } - // We have successfully asked the node to be a destination. - EitherOutput::Second((to_dest_substream, from_dst_read_buffer)) => { - let incoming_relay_req = match open_info { - RelayOutboundOpenInfo::Destination { - incoming_relay_req, .. - } => incoming_relay_req, - RelayOutboundOpenInfo::Relay { .. } => unreachable!( - "Can not successfully dial a destination when actually dialing a relay." - ), - }; - // TODO: Should this not be driven by the src handler? - self.copy_futures - .push(incoming_relay_req.fulfill(to_dest_substream, from_dst_read_buffer)); - } - } - } - - fn inject_event(&mut self, event: Self::InEvent) { - match event { - RelayHandlerIn::UsedForListening(s) => self.used_for_listening = s, - // Deny a relay request from the node we handle. - RelayHandlerIn::DenyIncomingRelayReq(req) => { - self.deny_futures.push(req); - } - RelayHandlerIn::AcceptDstReq(request) => self.accept_dst_futures.push(request.accept()), - RelayHandlerIn::DenyDstReq(request) => self.deny_futures.push(request.deny()), - // Ask the node we handle to act as a relay. - RelayHandlerIn::OutgoingRelayReq { - src_peer_id, - dst_peer_id, - request_id, - dst_addr, - } => { - self.outgoing_relay_reqs.push(OutgoingRelayReq { - src_peer_id, - dst_peer_id, - request_id, - dst_addr, - }); - } - // Ask the node we handle to act as a destination. - RelayHandlerIn::OutgoingDstReq { - src_peer_id, - src_addr, - src_connection_id, - request_id, - incoming_relay_req, - } => { - self.outgoing_dst_reqs.push(OutgoingDstReq { - src_peer_id, - src_addr, - src_connection_id, - request_id, - incoming_relay_req, - }); - } - } - } - - fn inject_listen_upgrade_error( - &mut self, - _: Self::InboundOpenInfo, - error: ConnectionHandlerUpgrErr, - ) { - match error { - ConnectionHandlerUpgrErr::Timeout | ConnectionHandlerUpgrErr::Timer => {} - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Select( - upgrade::NegotiationError::Failed, - )) => {} - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Select( - upgrade::NegotiationError::ProtocolError(e), - )) => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Select(upgrade::NegotiationError::ProtocolError(e)), - )); - } - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Apply(error)) => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Apply(EitherError::A(error)), - )) - } - } - } - - fn inject_dial_upgrade_error( - &mut self, - open_info: Self::OutboundOpenInfo, - error: ConnectionHandlerUpgrErr< - EitherError, - >, - ) { - match open_info { - RelayOutboundOpenInfo::Relay { - dst_peer_id, - request_id, - } => { - match error { - ConnectionHandlerUpgrErr::Timeout | ConnectionHandlerUpgrErr::Timer => {} - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Select( - upgrade::NegotiationError::Failed, - )) => {} - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Select( - upgrade::NegotiationError::ProtocolError(e), - )) => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Select( - upgrade::NegotiationError::ProtocolError(e), - ), - )); - } - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Apply( - EitherError::A(error), - )) => match error { - protocol::OutgoingRelayReqError::Decode(_) - | protocol::OutgoingRelayReqError::Io(_) - | protocol::OutgoingRelayReqError::ParseTypeField - | protocol::OutgoingRelayReqError::ParseStatusField - | protocol::OutgoingRelayReqError::UnexpectedSrcPeerWithStatusType - | protocol::OutgoingRelayReqError::UnexpectedDstPeerWithStatusType - | protocol::OutgoingRelayReqError::ExpectedStatusType(_) => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Apply(EitherError::B(EitherError::A(error))), - )); - } - protocol::OutgoingRelayReqError::ExpectedSuccessStatus(status) => { - match status { - circuit_relay::Status::Success => { - unreachable!("Status success was explicitly expected earlier.") - } - // With either status below there is no reason to stay connected. - // Thus terminate the connection. - circuit_relay::Status::HopSrcAddrTooLong - | circuit_relay::Status::HopSrcMultiaddrInvalid - | circuit_relay::Status::MalformedMessage => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Apply(EitherError::B( - EitherError::A(error), - )), - )); - } - // While useless for reaching this particular destination, the - // connection to the relay might still proof helpful for other - // destinations. Thus do not terminate the connection. - circuit_relay::Status::StopSrcAddrTooLong - | circuit_relay::Status::StopDstAddrTooLong - | circuit_relay::Status::StopSrcMultiaddrInvalid - | circuit_relay::Status::StopDstMultiaddrInvalid - | circuit_relay::Status::StopRelayRefused - | circuit_relay::Status::HopDstAddrTooLong - | circuit_relay::Status::HopDstMultiaddrInvalid - | circuit_relay::Status::HopNoConnToDst - | circuit_relay::Status::HopCantDialDst - | circuit_relay::Status::HopCantOpenDstStream - | circuit_relay::Status::HopCantSpeakRelay - | circuit_relay::Status::HopCantRelayToSelf => {} - } - } - }, - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Apply( - EitherError::B(_), - )) => { - unreachable!("Can not receive an OutgoingDstReqError when dialing a relay.") - } - } - - self.queued_events - .push(RelayHandlerEvent::OutgoingRelayReqError( - dst_peer_id, - request_id, - )); - } - RelayOutboundOpenInfo::Destination { - src_connection_id, - incoming_relay_req, - .. - } => { - let err_code = match error { - ConnectionHandlerUpgrErr::Timeout | ConnectionHandlerUpgrErr::Timer => { - circuit_relay::Status::HopCantOpenDstStream - } - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Select( - upgrade::NegotiationError::Failed, - )) => circuit_relay::Status::HopCantSpeakRelay, - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Select( - upgrade::NegotiationError::ProtocolError(e), - )) => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Select( - upgrade::NegotiationError::ProtocolError(e), - ), - )); - circuit_relay::Status::HopCantSpeakRelay - } - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Apply( - EitherError::A(_), - )) => unreachable!( - "Can not receive an OutgoingRelayReqError when dialing a destination." - ), - ConnectionHandlerUpgrErr::Upgrade(upgrade::UpgradeError::Apply( - EitherError::B(error), - )) => { - match error { - protocol::OutgoingDstReqError::Decode(_) - | protocol::OutgoingDstReqError::Io(_) - | protocol::OutgoingDstReqError::ParseTypeField - | protocol::OutgoingDstReqError::ParseStatusField - | protocol::OutgoingDstReqError::UnexpectedSrcPeerWithStatusType - | protocol::OutgoingDstReqError::UnexpectedDstPeerWithStatusType - | protocol::OutgoingDstReqError::ExpectedStatusType(_) => { - self.pending_error = Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Apply(EitherError::B(EitherError::B( - error, - ))), - )); - circuit_relay::Status::HopCantOpenDstStream - } - protocol::OutgoingDstReqError::ExpectedSuccessStatus(status) => { - match status { - circuit_relay::Status::Success => { - unreachable!( - "Status success was explicitly expected earlier." - ) - } - // A destination node returning `Hop.*` status is a protocol - // violation. Thus terminate the connection. - circuit_relay::Status::HopDstAddrTooLong - | circuit_relay::Status::HopDstMultiaddrInvalid - | circuit_relay::Status::HopNoConnToDst - | circuit_relay::Status::HopCantDialDst - | circuit_relay::Status::HopCantOpenDstStream - | circuit_relay::Status::HopCantSpeakRelay - | circuit_relay::Status::HopCantRelayToSelf - | circuit_relay::Status::HopSrcAddrTooLong - | circuit_relay::Status::HopSrcMultiaddrInvalid => { - self.pending_error = - Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Apply(EitherError::B( - EitherError::B(error), - )), - )); - } - // With either status below there is no reason to stay connected. - // Thus terminate the connection. - circuit_relay::Status::StopDstAddrTooLong - | circuit_relay::Status::StopDstMultiaddrInvalid - | circuit_relay::Status::MalformedMessage => { - self.pending_error = - Some(ConnectionHandlerUpgrErr::Upgrade( - upgrade::UpgradeError::Apply(EitherError::B( - EitherError::B(error), - )), - )); - } - // While useless for reaching this particular destination, the - // connection to the relay might still proof helpful for other - // destinations. Thus do not terminate the connection. - circuit_relay::Status::StopSrcAddrTooLong - | circuit_relay::Status::StopSrcMultiaddrInvalid - | circuit_relay::Status::StopRelayRefused => {} - } - status - } - } - } - }; - - self.queued_events - .push(RelayHandlerEvent::OutgoingDstReqError { - src_connection_id, - incoming_relay_req_deny_fut: incoming_relay_req.deny(err_code), - }); - } - } - } - - fn connection_keep_alive(&self) -> KeepAlive { - self.keep_alive - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::OutEvent, - Self::Error, - >, - > { - // Check for a pending (fatal) error. - if let Some(err) = self.pending_error.take() { - // The handler will not be polled again by the `Swarm`. - return Poll::Ready(ConnectionHandlerEvent::Close(err)); - } - - // Request the remote to act as a relay. - if !self.outgoing_relay_reqs.is_empty() { - let OutgoingRelayReq { - src_peer_id, - dst_peer_id, - request_id, - dst_addr, - } = self.outgoing_relay_reqs.remove(0); - self.outgoing_relay_reqs.shrink_to_fit(); - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new( - upgrade::EitherUpgrade::A(protocol::OutgoingRelayReq::new( - src_peer_id, - dst_peer_id, - dst_addr, - )), - RelayOutboundOpenInfo::Relay { - dst_peer_id, - request_id, - }, - ), - }); - } - - // Request the remote to act as destination. - if !self.outgoing_dst_reqs.is_empty() { - let OutgoingDstReq { - src_peer_id, - src_addr, - src_connection_id, - request_id, - incoming_relay_req, - } = self.outgoing_dst_reqs.remove(0); - self.outgoing_dst_reqs.shrink_to_fit(); - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new( - upgrade::EitherUpgrade::B(protocol::OutgoingDstReq::new( - src_peer_id, - src_addr, - incoming_relay_req.dst_peer().clone(), - )), - RelayOutboundOpenInfo::Destination { - src_peer_id, - request_id, - src_connection_id, - incoming_relay_req, - }, - ), - }); - } - - match self.accept_dst_futures.poll_next_unpin(cx) { - Poll::Ready(Some(Ok((src_peer_id, substream, notifyee)))) => { - self.alive_lend_out_substreams.push(notifyee); - let event = RelayHandlerEvent::IncomingDstReqSuccess { - stream: substream, - src_peer_id, - relay_peer_id: self.remote_peer_id, - relay_addr: self.remote_address.clone(), - }; - return Poll::Ready(ConnectionHandlerEvent::Custom(event)); - } - Poll::Ready(Some(Err(e))) => { - log::debug!("Failed to accept destination future: {:?}", e); - } - Poll::Ready(None) => {} - Poll::Pending => {} - } - - while let Poll::Ready(Some(result)) = self.copy_futures.poll_next_unpin(cx) { - if let Err(e) = result { - warn!("Incoming relay request failed: {:?}", e); - } - } - - while let Poll::Ready(Some(result)) = self.deny_futures.poll_next_unpin(cx) { - if let Err(e) = result { - warn!("Denying request failed: {:?}", e); - } - } - - // Report the queued events. - if !self.queued_events.is_empty() { - let event = self.queued_events.remove(0); - return Poll::Ready(ConnectionHandlerEvent::Custom(event)); - } - - while let Poll::Ready(Some(Err(Canceled))) = - self.alive_lend_out_substreams.poll_next_unpin(cx) - {} - - if self.used_for_listening - || !self.deny_futures.is_empty() - || !self.accept_dst_futures.is_empty() - || !self.copy_futures.is_empty() - || !self.alive_lend_out_substreams.is_empty() - { - // Protocol handler is busy. - self.keep_alive = KeepAlive::Yes; - } else { - // Protocol handler is idle. - if matches!(self.keep_alive, KeepAlive::Yes) { - self.keep_alive = - KeepAlive::Until(Instant::now() + self.config.connection_idle_timeout); - } - } - - Poll::Pending - } -} - -#[allow(clippy::large_enum_variant)] -pub enum RelayOutboundOpenInfo { - Relay { - dst_peer_id: PeerId, - request_id: RequestId, - }, - Destination { - src_peer_id: PeerId, - src_connection_id: ConnectionId, - request_id: RequestId, - incoming_relay_req: protocol::IncomingRelayReq, - }, -} diff --git a/protocols/relay/src/v1/message.proto b/protocols/relay/src/v1/message.proto deleted file mode 100644 index 0366c9ef9ca..00000000000 --- a/protocols/relay/src/v1/message.proto +++ /dev/null @@ -1,43 +0,0 @@ -syntax = "proto2"; -package message_v1.pb; - -message CircuitRelay { - - enum Status { - SUCCESS = 100; - HOP_SRC_ADDR_TOO_LONG = 220; - HOP_DST_ADDR_TOO_LONG = 221; - HOP_SRC_MULTIADDR_INVALID = 250; - HOP_DST_MULTIADDR_INVALID = 251; - HOP_NO_CONN_TO_DST = 260; - HOP_CANT_DIAL_DST = 261; - HOP_CANT_OPEN_DST_STREAM = 262; - HOP_CANT_SPEAK_RELAY = 270; - HOP_CANT_RELAY_TO_SELF = 280; - STOP_SRC_ADDR_TOO_LONG = 320; - STOP_DST_ADDR_TOO_LONG = 321; - STOP_SRC_MULTIADDR_INVALID = 350; - STOP_DST_MULTIADDR_INVALID = 351; - STOP_RELAY_REFUSED = 390; - MALFORMED_MESSAGE = 400; - } - - enum Type { // RPC identifier, either HOP, STOP or STATUS - HOP = 1; - STOP = 2; - STATUS = 3; - CAN_HOP = 4; // is peer a relay? - } - - message Peer { - required bytes id = 1; // peer id - repeated bytes addrs = 2; // peer's known addresses - } - - optional Type type = 1; // Type of the message - - optional Peer srcPeer = 2; // srcPeer and dstPeer are used when Type is HOP or STOP - optional Peer dstPeer = 3; - - optional Status code = 4; // Status code, used when Type is STATUS -} diff --git a/protocols/relay/src/v1/protocol.rs b/protocols/relay/src/v1/protocol.rs deleted file mode 100644 index 274ad39d3ac..00000000000 --- a/protocols/relay/src/v1/protocol.rs +++ /dev/null @@ -1,105 +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 crate::v1::message_proto::circuit_relay; - -use libp2p_core::{multiaddr::Error as MultiaddrError, Multiaddr, PeerId}; -use smallvec::SmallVec; -use std::{convert::TryFrom, error, fmt}; - -// Source -> Relay -mod incoming_relay_req; -mod outgoing_relay_req; -pub use self::incoming_relay_req::{IncomingRelayReq, IncomingRelayReqError}; -pub use self::outgoing_relay_req::{OutgoingRelayReq, OutgoingRelayReqError}; - -// Relay -> Destination -mod incoming_dst_req; -mod outgoing_dst_req; -pub use self::incoming_dst_req::{IncomingDstReq, IncomingDstReqError}; -pub use self::outgoing_dst_req::{OutgoingDstReq, OutgoingDstReqError}; - -mod listen; -pub use self::listen::{RelayListen, RelayListenError, RelayRemoteReq}; - -/// Any message received on the wire whose length exceeds this value is refused. -// -// The circuit relay specification sets a maximum of 1024 bytes per multiaddr. A single message can -// contain multiple addresses for both the source and destination node. Setting the maximum message -// length to 10 times that limit is an unproven estimate. Feel free to refine this in the future. -const MAX_ACCEPTED_MESSAGE_LEN: usize = 10 * 1024; - -const PROTOCOL_NAME: &[u8; 27] = b"/libp2p/circuit/relay/0.1.0"; - -/// Representation of a `CircuitRelay_Peer` protobuf message with refined field types. -/// -/// Can be parsed from a `CircuitRelay_Peer` using the `TryFrom` trait. -#[derive(Clone)] -pub(crate) struct Peer { - pub(crate) peer_id: PeerId, - pub(crate) addrs: SmallVec<[Multiaddr; 4]>, -} - -impl TryFrom for Peer { - type Error = PeerParseError; - - fn try_from(peer: circuit_relay::Peer) -> Result { - let circuit_relay::Peer { id, addrs } = peer; - let peer_id = PeerId::from_bytes(&id).map_err(|_| PeerParseError::PeerIdParseError)?; - let mut parsed_addrs = SmallVec::with_capacity(addrs.len()); - for addr in addrs.into_iter() { - let addr = Multiaddr::try_from(addr).map_err(PeerParseError::MultiaddrParseError)?; - parsed_addrs.push(addr); - } - Ok(Peer { - peer_id, - addrs: parsed_addrs, - }) - } -} - -/// Error while parsing information about a peer from a network message. -#[derive(Debug)] -pub enum PeerParseError { - /// Failed to parse the identity of the peer. - PeerIdParseError, - /// Failed to parse one of the multiaddresses for the peer. - MultiaddrParseError(MultiaddrError), -} - -impl fmt::Display for PeerParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - PeerParseError::PeerIdParseError => write!(f, "Error while parsing the peer id"), - PeerParseError::MultiaddrParseError(ref err) => { - write!(f, "Error while parsing a multiaddress: {}", err) - } - } - } -} - -impl error::Error for PeerParseError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - PeerParseError::PeerIdParseError => None, - PeerParseError::MultiaddrParseError(ref err) => Some(err), - } - } -} diff --git a/protocols/relay/src/v1/protocol/incoming_dst_req.rs b/protocols/relay/src/v1/protocol/incoming_dst_req.rs deleted file mode 100644 index 21ed1cb83a8..00000000000 --- a/protocols/relay/src/v1/protocol/incoming_dst_req.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2019 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::v1::message_proto::{circuit_relay, CircuitRelay}; -use crate::v1::protocol::Peer; -use crate::v1::Connection; - -use asynchronous_codec::{Framed, FramedParts}; -use bytes::BytesMut; -use futures::channel::oneshot; -use futures::{future::BoxFuture, prelude::*}; -use libp2p_core::{Multiaddr, PeerId}; -use libp2p_swarm::NegotiatedSubstream; -use prost::Message; -use std::io; -use unsigned_varint::codec::UviBytes; - -/// Request from a remote for us to become a destination. -/// -/// If we take a situation where a *source* wants to talk to a *destination* through a *relay*, and -/// we are the *destination*, this struct is a message that the *relay* sent to us. The -/// parameters passed to `IncomingDstReq::new()` are the information of the *source*. -/// -/// If the upgrade succeeds, the substream is returned and we will receive data sent from the -/// source on it. -#[must_use = "An incoming destination request should be either accepted or denied"] -pub struct IncomingDstReq { - /// The stream to the source. - stream: Framed, - /// Source of the request. - src: Peer, -} - -impl IncomingDstReq { - /// Creates a `IncomingDstReq`. - pub(crate) fn new(stream: Framed, src: Peer) -> Self { - IncomingDstReq { stream, src } - } - - /// Returns the peer id of the source that is being relayed. - pub fn src_id(&self) -> &PeerId { - &self.src.peer_id - } - - /// Returns the addresses of the source that is being relayed. - pub fn src_addrs(&self) -> impl Iterator { - self.src.addrs.iter() - } - - /// Accepts the request. - /// - /// The returned `Future` sends back a success message then returns the raw stream. This raw - /// stream then points to the source (as retreived with `src_id()` and `src_addrs()`). - pub fn accept( - self, - ) -> BoxFuture<'static, Result<(PeerId, Connection, oneshot::Receiver<()>), IncomingDstReqError>> - { - let IncomingDstReq { mut stream, src } = self; - let msg = CircuitRelay { - r#type: Some(circuit_relay::Type::Status.into()), - src_peer: None, - dst_peer: None, - code: Some(circuit_relay::Status::Success.into()), - }; - let mut msg_bytes = BytesMut::new(); - msg.encode(&mut msg_bytes) - .expect("all the mandatory fields are always filled; QED"); - - async move { - stream.send(msg_bytes.freeze()).await?; - - let FramedParts { - io, - read_buffer, - write_buffer, - .. - } = stream.into_parts(); - assert!( - write_buffer.is_empty(), - "Expect a flushed Framed to have empty write buffer." - ); - - let (tx, rx) = oneshot::channel(); - - Ok(( - src.peer_id, - Connection::new(read_buffer.freeze(), io, tx), - rx, - )) - } - .boxed() - } - - /// Refuses the request. - /// - /// The returned `Future` gracefully shuts down the request. - pub fn deny(mut self) -> BoxFuture<'static, Result<(), io::Error>> { - let msg = CircuitRelay { - r#type: Some(circuit_relay::Type::Status.into()), - src_peer: None, - dst_peer: None, - code: Some(circuit_relay::Status::StopRelayRefused.into()), - }; - let mut msg_bytes = BytesMut::new(); - msg.encode(&mut msg_bytes) - .expect("all the mandatory fields are always filled; QED"); - - async move { - self.stream.send(msg_bytes.freeze()).await?; - Ok(()) - } - .boxed() - } -} - -#[derive(Debug)] -pub enum IncomingDstReqError { - Io(std::io::Error), -} - -impl From for IncomingDstReqError { - fn from(e: std::io::Error) -> Self { - IncomingDstReqError::Io(e) - } -} diff --git a/protocols/relay/src/v1/protocol/incoming_relay_req.rs b/protocols/relay/src/v1/protocol/incoming_relay_req.rs deleted file mode 100644 index 97f1b96ce3f..00000000000 --- a/protocols/relay/src/v1/protocol/incoming_relay_req.rs +++ /dev/null @@ -1,155 +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 crate::v1::copy_future::CopyFuture; -use crate::v1::message_proto::{circuit_relay, circuit_relay::Status, CircuitRelay}; -use crate::v1::protocol::Peer; - -use asynchronous_codec::{Framed, FramedParts}; -use bytes::{Bytes, BytesMut}; -use futures::channel::oneshot; -use futures::future::BoxFuture; -use futures::prelude::*; -use libp2p_swarm::NegotiatedSubstream; -use prost::Message; -use std::time::Duration; -use unsigned_varint::codec::UviBytes; - -/// Request from a remote for us to relay communications to another node. -/// -/// If we take a situation where a *source* wants to talk to a *destination* through a *relay*, and -/// we are the *relay*, this struct is a message that the *source* sent to us. The parameters -/// passed to `IncomingRelayReq::new()` are the information of the *destination*. -/// -/// If the upgrade succeeds, the substream is returned and we will receive data sent from the -/// source on it. This data must be transmitted to the destination. -#[must_use = "An incoming relay request should be either accepted or denied."] -pub struct IncomingRelayReq { - /// The stream to the source. - stream: Framed, - /// Target of the request. - dest: Peer, - - _notifier: oneshot::Sender<()>, -} - -impl IncomingRelayReq { - /// Creates a [`IncomingRelayReq`] as well as a Future that resolves once the - /// [`IncomingRelayReq`] is dropped. - pub(crate) fn new( - stream: Framed, - dest: Peer, - ) -> (Self, oneshot::Receiver<()>) { - let (tx, rx) = oneshot::channel(); - ( - IncomingRelayReq { - stream, - dest, - _notifier: tx, - }, - rx, - ) - } - - /// Peer id of the node we should relay communications to. - pub(crate) fn dst_peer(&self) -> &Peer { - &self.dest - } - - /// Accepts the request by providing a stream to the destination. - pub fn fulfill( - mut self, - dst_stream: TDestSubstream, - dst_read_buffer: Bytes, - ) -> BoxFuture<'static, Result<(), IncomingRelayReqError>> - where - TDestSubstream: AsyncRead + AsyncWrite + Send + Unpin + 'static, - { - let msg = CircuitRelay { - r#type: Some(circuit_relay::Type::Status.into()), - src_peer: None, - dst_peer: None, - code: Some(circuit_relay::Status::Success.into()), - }; - let mut msg_bytes = BytesMut::new(); - msg.encode(&mut msg_bytes) - .expect("all the mandatory fields are always filled; QED"); - - async move { - self.stream.send(msg_bytes.freeze()).await?; - - let FramedParts { - mut io, - read_buffer, - write_buffer, - .. - } = self.stream.into_parts(); - assert!( - read_buffer.is_empty(), - "Expect a Framed, that was never actively read from, not to read." - ); - assert!( - write_buffer.is_empty(), - "Expect a flushed Framed to have empty write buffer." - ); - - if !dst_read_buffer.is_empty() { - io.write_all(&dst_read_buffer).await?; - } - - let copy_future = CopyFuture::new(io, dst_stream, Duration::from_secs(5)); - - copy_future.await.map_err(Into::into) - } - .boxed() - } - - /// Refuses the request. - /// - /// The returned `Future` gracefully shuts down the request. - pub fn deny(mut self, err_code: Status) -> BoxFuture<'static, Result<(), std::io::Error>> { - let msg = CircuitRelay { - r#type: Some(circuit_relay::Type::Status.into()), - code: Some(err_code.into()), - src_peer: None, - dst_peer: None, - }; - let mut msg_bytes = BytesMut::new(); - msg.encode(&mut msg_bytes) - .expect("all the mandatory fields are always filled; QED"); - - async move { - self.stream.send(msg_bytes.freeze()).await?; - Ok(()) - } - .boxed() - } -} - -#[derive(Debug)] -pub enum IncomingRelayReqError { - Io(std::io::Error), -} - -impl From for IncomingRelayReqError { - fn from(e: std::io::Error) -> Self { - IncomingRelayReqError::Io(e) - } -} diff --git a/protocols/relay/src/v1/protocol/listen.rs b/protocols/relay/src/v1/protocol/listen.rs deleted file mode 100644 index 87d77458050..00000000000 --- a/protocols/relay/src/v1/protocol/listen.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2019 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::v1::message_proto::{circuit_relay, CircuitRelay}; -use crate::v1::protocol::incoming_dst_req::IncomingDstReq; -use crate::v1::protocol::incoming_relay_req::IncomingRelayReq; -use crate::v1::protocol::{Peer, PeerParseError}; -use crate::v1::protocol::{MAX_ACCEPTED_MESSAGE_LEN, PROTOCOL_NAME}; -use asynchronous_codec::Framed; -use futures::channel::oneshot; -use futures::{future::BoxFuture, prelude::*}; -use libp2p_core::upgrade; -use libp2p_swarm::NegotiatedSubstream; -use prost::Message; - -use std::io::Cursor; -use std::{convert::TryFrom, error, fmt, iter}; -use unsigned_varint::codec::UviBytes; - -/// Configuration for an inbound upgrade that handles requests from the remote for the relay -/// protocol. -#[derive(Debug, Clone)] -pub struct RelayListen {} - -/// Outcome of the listening. -pub enum RelayRemoteReq { - /// We have been asked to become a destination. - DstReq(IncomingDstReq), - /// We have been asked to relay communications to another node. - RelayReq((IncomingRelayReq, oneshot::Receiver<()>)), -} - -impl RelayListen { - /// Builds a new `RelayListen` with default options. - pub fn new() -> RelayListen { - RelayListen {} - } -} - -impl upgrade::UpgradeInfo for RelayListen { - type Info = &'static [u8]; - type InfoIter = iter::Once; - - fn protocol_info(&self) -> Self::InfoIter { - iter::once(PROTOCOL_NAME) - } -} - -impl upgrade::InboundUpgrade for RelayListen { - type Output = RelayRemoteReq; - type Error = RelayListenError; - type Future = BoxFuture<'static, Result>; - - fn upgrade_inbound(self, substream: NegotiatedSubstream, _: Self::Info) -> Self::Future { - async move { - let mut codec = UviBytes::::default(); - codec.set_max_len(MAX_ACCEPTED_MESSAGE_LEN); - let mut substream = Framed::new(substream, codec); - - let msg: bytes::BytesMut = substream - .next() - .await - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))??; - let CircuitRelay { - r#type, - src_peer, - dst_peer, - code: _, - } = CircuitRelay::decode(Cursor::new(msg))?; - - match circuit_relay::Type::from_i32(r#type.ok_or(RelayListenError::NoMessageType)?) - .ok_or(RelayListenError::InvalidMessageTy)? - { - circuit_relay::Type::Hop => { - let peer = Peer::try_from(dst_peer.ok_or(RelayListenError::NoDstPeer)?)?; - let (rq, notifyee) = IncomingRelayReq::new(substream, peer); - Ok(RelayRemoteReq::RelayReq((rq, notifyee))) - } - circuit_relay::Type::Stop => { - let peer = Peer::try_from(src_peer.ok_or(RelayListenError::NoSrcPeer)?)?; - let rq = IncomingDstReq::new(substream, peer); - Ok(RelayRemoteReq::DstReq(rq)) - } - _ => Err(RelayListenError::InvalidMessageTy), - } - } - .boxed() - } -} - -/// Error while upgrading with a [`RelayListen`]. -#[derive(Debug)] -pub enum RelayListenError { - Decode(prost::DecodeError), - Io(std::io::Error), - NoSrcPeer, - NoDstPeer, - ParsePeer(PeerParseError), - NoMessageType, - InvalidMessageTy, -} - -impl From for RelayListenError { - fn from(err: prost::DecodeError) -> Self { - RelayListenError::Decode(err) - } -} - -impl From for RelayListenError { - fn from(e: std::io::Error) -> Self { - RelayListenError::Io(e) - } -} - -impl From for RelayListenError { - fn from(err: PeerParseError) -> Self { - RelayListenError::ParsePeer(err) - } -} - -impl fmt::Display for RelayListenError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RelayListenError::Decode(e) => { - write!(f, "Failed to decode response: {}.", e) - } - RelayListenError::Io(e) => { - write!(f, "Io error {}", e) - } - RelayListenError::NoSrcPeer => { - write!(f, "Expected source peer id") - } - RelayListenError::NoDstPeer => { - write!(f, "Expected destination peer id") - } - RelayListenError::ParsePeer(e) => { - write!(f, "Failed to parse peer field: {}", e) - } - RelayListenError::NoMessageType => { - write!(f, "Expected message type to be set.") - } - RelayListenError::InvalidMessageTy => { - write!(f, "Invalid message type") - } - } - } -} - -impl error::Error for RelayListenError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - RelayListenError::Decode(e) => Some(e), - RelayListenError::Io(e) => Some(e), - RelayListenError::NoSrcPeer => None, - RelayListenError::NoDstPeer => None, - RelayListenError::ParsePeer(_) => None, - RelayListenError::NoMessageType => None, - RelayListenError::InvalidMessageTy => None, - } - } -} diff --git a/protocols/relay/src/v1/protocol/outgoing_dst_req.rs b/protocols/relay/src/v1/protocol/outgoing_dst_req.rs deleted file mode 100644 index 1cd3e78d18d..00000000000 --- a/protocols/relay/src/v1/protocol/outgoing_dst_req.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2019 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::v1::message_proto::{circuit_relay, CircuitRelay}; -use crate::v1::protocol::Peer; -use crate::v1::protocol::{MAX_ACCEPTED_MESSAGE_LEN, PROTOCOL_NAME}; -use asynchronous_codec::{Framed, FramedParts}; -use bytes::Bytes; -use futures::future::BoxFuture; -use futures::prelude::*; -use libp2p_core::{upgrade, Multiaddr, PeerId}; -use libp2p_swarm::NegotiatedSubstream; -use prost::Message; -use std::{error, fmt, iter}; -use unsigned_varint::codec::UviBytes; - -/// Ask the remote to become a destination. The upgrade succeeds if the remote accepts, and fails -/// if the remote refuses. -/// -/// If we take a situation where a *source* wants to talk to a *destination* through a *relay*, -/// this struct is the message that the *relay* sends to the *destination* at initialization. The -/// parameters passed to `OutgoingDstReq::new()` are the information of the *source* and the -/// *destination* (not the information of the *relay*). -/// -/// The upgrade should be performed on a substream to the *destination*. -/// -/// If the upgrade succeeds, the substream is returned and we must link it with the data sent from -/// the source. -#[derive(Debug, Clone)] -pub struct OutgoingDstReq { - /// The message to send to the destination. Pre-computed. - message: Vec, -} - -impl OutgoingDstReq { - /// Creates a `OutgoingDstReq`. Must pass the parameters of the message. - pub(crate) fn new(src_id: PeerId, src_addr: Multiaddr, dst_peer: Peer) -> Self { - let message = CircuitRelay { - r#type: Some(circuit_relay::Type::Stop.into()), - src_peer: Some(circuit_relay::Peer { - id: src_id.to_bytes(), - addrs: vec![src_addr.to_vec()], - }), - dst_peer: Some(circuit_relay::Peer { - id: dst_peer.peer_id.to_bytes(), - addrs: dst_peer.addrs.into_iter().map(|a| a.to_vec()).collect(), - }), - code: None, - }; - let mut encoded_msg = Vec::new(); - message - .encode(&mut encoded_msg) - .expect("all the mandatory fields are always filled; QED"); - - OutgoingDstReq { - message: encoded_msg, - } - } -} - -impl upgrade::UpgradeInfo for OutgoingDstReq { - type Info = &'static [u8]; - type InfoIter = iter::Once; - - fn protocol_info(&self) -> Self::InfoIter { - iter::once(PROTOCOL_NAME) - } -} - -impl upgrade::OutboundUpgrade for OutgoingDstReq { - type Output = (NegotiatedSubstream, Bytes); - type Error = OutgoingDstReqError; - type Future = BoxFuture<'static, Result>; - - fn upgrade_outbound(self, substream: NegotiatedSubstream, _: Self::Info) -> Self::Future { - let mut codec = UviBytes::default(); - codec.set_max_len(MAX_ACCEPTED_MESSAGE_LEN); - - let mut substream = Framed::new(substream, codec); - - async move { - substream.send(std::io::Cursor::new(self.message)).await?; - let msg = substream.next().await.ok_or_else(|| { - OutgoingDstReqError::Io(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "")) - })??; - - let msg = std::io::Cursor::new(msg); - let CircuitRelay { - r#type, - src_peer, - dst_peer, - code, - } = CircuitRelay::decode(msg)?; - - match r#type - .and_then(circuit_relay::Type::from_i32) - .ok_or(OutgoingDstReqError::ParseTypeField)? - { - circuit_relay::Type::Status => {} - s => return Err(OutgoingDstReqError::ExpectedStatusType(s)), - } - - if src_peer.is_some() { - return Err(OutgoingDstReqError::UnexpectedSrcPeerWithStatusType); - } - if dst_peer.is_some() { - return Err(OutgoingDstReqError::UnexpectedDstPeerWithStatusType); - } - - match code - .and_then(circuit_relay::Status::from_i32) - .ok_or(OutgoingDstReqError::ParseStatusField)? - { - circuit_relay::Status::Success => {} - s => return Err(OutgoingDstReqError::ExpectedSuccessStatus(s)), - } - - let FramedParts { - io, - read_buffer, - write_buffer, - .. - } = substream.into_parts(); - assert!( - write_buffer.is_empty(), - "Expect a flushed Framed to have an empty write buffer." - ); - - Ok((io, read_buffer.freeze())) - } - .boxed() - } -} - -#[derive(Debug)] -pub enum OutgoingDstReqError { - Decode(prost::DecodeError), - Io(std::io::Error), - ParseTypeField, - ParseStatusField, - ExpectedStatusType(circuit_relay::Type), - ExpectedSuccessStatus(circuit_relay::Status), - UnexpectedSrcPeerWithStatusType, - UnexpectedDstPeerWithStatusType, -} - -impl From for OutgoingDstReqError { - fn from(e: std::io::Error) -> Self { - OutgoingDstReqError::Io(e) - } -} - -impl From for OutgoingDstReqError { - fn from(e: prost::DecodeError) -> Self { - OutgoingDstReqError::Decode(e) - } -} - -impl fmt::Display for OutgoingDstReqError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - OutgoingDstReqError::Decode(e) => { - write!(f, "Failed to decode response: {}.", e) - } - OutgoingDstReqError::Io(e) => { - write!(f, "Io error {}", e) - } - OutgoingDstReqError::ParseTypeField => { - write!(f, "Failed to parse response type field.") - } - OutgoingDstReqError::ParseStatusField => { - write!(f, "Failed to parse response status field.") - } - OutgoingDstReqError::ExpectedStatusType(t) => { - write!(f, "Expected status message type, but got {:?}", t) - } - OutgoingDstReqError::UnexpectedSrcPeerWithStatusType => { - write!(f, "Unexpected source peer with status type.") - } - OutgoingDstReqError::UnexpectedDstPeerWithStatusType => { - write!(f, "Unexpected destination peer with status type.") - } - OutgoingDstReqError::ExpectedSuccessStatus(s) => { - write!(f, "Expected success status but got {:?}", s) - } - } - } -} - -impl error::Error for OutgoingDstReqError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - OutgoingDstReqError::Decode(e) => Some(e), - OutgoingDstReqError::Io(e) => Some(e), - OutgoingDstReqError::ParseTypeField => None, - OutgoingDstReqError::ParseStatusField => None, - OutgoingDstReqError::ExpectedStatusType(_) => None, - OutgoingDstReqError::UnexpectedSrcPeerWithStatusType => None, - OutgoingDstReqError::UnexpectedDstPeerWithStatusType => None, - OutgoingDstReqError::ExpectedSuccessStatus(_) => None, - } - } -} diff --git a/protocols/relay/src/v1/protocol/outgoing_relay_req.rs b/protocols/relay/src/v1/protocol/outgoing_relay_req.rs deleted file mode 100644 index f67a57712dd..00000000000 --- a/protocols/relay/src/v1/protocol/outgoing_relay_req.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019 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::v1::message_proto::{circuit_relay, CircuitRelay}; -use crate::v1::protocol::{MAX_ACCEPTED_MESSAGE_LEN, PROTOCOL_NAME}; -use crate::v1::Connection; -use asynchronous_codec::{Framed, FramedParts}; -use futures::channel::oneshot; -use futures::future::BoxFuture; -use futures::prelude::*; -use libp2p_core::{upgrade, Multiaddr, PeerId}; -use libp2p_swarm::NegotiatedSubstream; -use prost::Message; -use std::{error, fmt, iter}; -use unsigned_varint::codec::UviBytes; - -/// Ask a remote to act as a relay. -/// -/// If we take a situation 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 `OutgoingRelayReq::new()` are the information of the *destination* -/// (not the information of the *relay*). -/// -/// 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*. -pub struct OutgoingRelayReq { - src_id: PeerId, - dst_id: PeerId, - dst_address: Option, -} - -impl OutgoingRelayReq { - /// Builds a request for the target to act as a relay to a third party. - pub fn new(src_id: PeerId, dst_id: PeerId, dst_address: Option) -> Self { - OutgoingRelayReq { - src_id, - dst_id, - dst_address, - } - } -} - -impl upgrade::UpgradeInfo for OutgoingRelayReq { - type Info = &'static [u8]; - type InfoIter = iter::Once; - - fn protocol_info(&self) -> Self::InfoIter { - iter::once(PROTOCOL_NAME) - } -} - -impl upgrade::OutboundUpgrade for OutgoingRelayReq { - type Output = (Connection, oneshot::Receiver<()>); - type Error = OutgoingRelayReqError; - type Future = BoxFuture<'static, Result>; - - fn upgrade_outbound(self, substream: NegotiatedSubstream, _: Self::Info) -> Self::Future { - let OutgoingRelayReq { - src_id, - dst_id, - dst_address, - } = self; - - let message = CircuitRelay { - r#type: Some(circuit_relay::Type::Hop.into()), - src_peer: Some(circuit_relay::Peer { - id: src_id.to_bytes(), - addrs: vec![], - }), - dst_peer: Some(circuit_relay::Peer { - id: dst_id.to_bytes(), - addrs: vec![dst_address.unwrap_or_else(Multiaddr::empty).to_vec()], - }), - code: None, - }; - let mut encoded = Vec::new(); - message - .encode(&mut encoded) - .expect("all the mandatory fields are always filled; QED"); - - let mut codec = UviBytes::default(); - codec.set_max_len(MAX_ACCEPTED_MESSAGE_LEN); - - let mut substream = Framed::new(substream, codec); - - async move { - substream.send(std::io::Cursor::new(encoded)).await?; - let msg = substream.next().await.ok_or_else(|| { - OutgoingRelayReqError::Io(std::io::Error::new( - std::io::ErrorKind::UnexpectedEof, - "", - )) - })??; - - let msg = std::io::Cursor::new(msg); - let CircuitRelay { - r#type, - src_peer, - dst_peer, - code, - } = CircuitRelay::decode(msg)?; - - match r#type - .and_then(circuit_relay::Type::from_i32) - .ok_or(OutgoingRelayReqError::ParseTypeField)? - { - circuit_relay::Type::Status => {} - s => return Err(OutgoingRelayReqError::ExpectedStatusType(s)), - } - - match code - .and_then(circuit_relay::Status::from_i32) - .ok_or(OutgoingRelayReqError::ParseStatusField)? - { - circuit_relay::Status::Success => {} - e => return Err(OutgoingRelayReqError::ExpectedSuccessStatus(e)), - } - - if src_peer.is_some() { - return Err(OutgoingRelayReqError::UnexpectedSrcPeerWithStatusType); - } - if dst_peer.is_some() { - return Err(OutgoingRelayReqError::UnexpectedDstPeerWithStatusType); - } - - let FramedParts { - io, - read_buffer, - write_buffer, - .. - } = substream.into_parts(); - assert!( - write_buffer.is_empty(), - "Expect a flushed Framed to have empty write buffer." - ); - - let (tx, rx) = oneshot::channel(); - - Ok((Connection::new(read_buffer.freeze(), io, tx), rx)) - } - .boxed() - } -} - -#[derive(Debug)] -pub enum OutgoingRelayReqError { - Decode(prost::DecodeError), - Io(std::io::Error), - ParseTypeField, - ParseStatusField, - ExpectedStatusType(circuit_relay::Type), - UnexpectedSrcPeerWithStatusType, - UnexpectedDstPeerWithStatusType, - ExpectedSuccessStatus(circuit_relay::Status), -} - -impl From for OutgoingRelayReqError { - fn from(e: std::io::Error) -> Self { - OutgoingRelayReqError::Io(e) - } -} - -impl From for OutgoingRelayReqError { - fn from(e: prost::DecodeError) -> Self { - OutgoingRelayReqError::Decode(e) - } -} - -impl fmt::Display for OutgoingRelayReqError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - OutgoingRelayReqError::Decode(e) => { - write!(f, "Failed to decode response: {}.", e) - } - OutgoingRelayReqError::Io(e) => { - write!(f, "Io error {}", e) - } - OutgoingRelayReqError::ParseTypeField => { - write!(f, "Failed to parse response type field.") - } - OutgoingRelayReqError::ParseStatusField => { - write!(f, "Failed to parse response status field.") - } - OutgoingRelayReqError::ExpectedStatusType(t) => { - write!(f, "Expected status message type, but got {:?}", t) - } - OutgoingRelayReqError::UnexpectedSrcPeerWithStatusType => { - write!(f, "Unexpected source peer with status type.") - } - OutgoingRelayReqError::UnexpectedDstPeerWithStatusType => { - write!(f, "Unexpected destination peer with status type.") - } - OutgoingRelayReqError::ExpectedSuccessStatus(s) => { - write!(f, "Expected success status but got {:?}", s) - } - } - } -} - -impl error::Error for OutgoingRelayReqError { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - OutgoingRelayReqError::Decode(e) => Some(e), - OutgoingRelayReqError::Io(e) => Some(e), - OutgoingRelayReqError::ParseTypeField => None, - OutgoingRelayReqError::ParseStatusField => None, - OutgoingRelayReqError::ExpectedStatusType(_) => None, - OutgoingRelayReqError::UnexpectedSrcPeerWithStatusType => None, - OutgoingRelayReqError::UnexpectedDstPeerWithStatusType => None, - OutgoingRelayReqError::ExpectedSuccessStatus(_) => None, - } - } -} diff --git a/protocols/relay/src/v1/transport.rs b/protocols/relay/src/v1/transport.rs deleted file mode 100644 index c96129155dc..00000000000 --- a/protocols/relay/src/v1/transport.rs +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright 2020 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::v1::behaviour::BehaviourToListenerMsg; -use crate::v1::{Connection, RequestId}; -use futures::channel::mpsc; -use futures::channel::oneshot; -use futures::future::{BoxFuture, Future, FutureExt}; -use futures::sink::SinkExt; -use futures::stream::{Stream, StreamExt}; -use libp2p_core::connection::Endpoint; -use libp2p_core::either::{EitherError, EitherFuture, EitherOutput}; -use libp2p_core::multiaddr::{Multiaddr, Protocol}; -use libp2p_core::transport::{ListenerEvent, TransportError}; -use libp2p_core::{PeerId, Transport}; -use pin_project::pin_project; -use std::pin::Pin; -use std::task::{Context, Poll}; - -/// A [`Transport`] wrapping another [`Transport`] enabling relay capabilities. -/// -/// Allows the local node to: -/// -/// 1. Use inner wrapped transport as before. -/// -/// ``` -/// # use libp2p_core::{Multiaddr, multiaddr::{Protocol}, Transport}; -/// # use libp2p_core::transport::memory::MemoryTransport; -/// # use libp2p_relay::v1::{RelayConfig, new_transport_and_behaviour}; -/// # let inner_transport = MemoryTransport::default(); -/// # let (relay_transport, relay_behaviour) = new_transport_and_behaviour( -/// # RelayConfig::default(), -/// # inner_transport, -/// # ); -/// relay_transport.dial(Multiaddr::empty().with(Protocol::Memory(42))); -/// ``` -/// -/// 2. Establish relayed connections by dialing `/p2p-circuit` addresses. -/// -/// ``` -/// # use libp2p_core::{Multiaddr, multiaddr::{Protocol}, PeerId, Transport}; -/// # use libp2p_core::transport::memory::MemoryTransport; -/// # use libp2p_relay::v1::{RelayConfig, new_transport_and_behaviour}; -/// # let inner_transport = MemoryTransport::default(); -/// # let (relay_transport, relay_behaviour) = new_transport_and_behaviour( -/// # RelayConfig::default(), -/// # inner_transport, -/// # ); -/// let dst_addr_via_relay = Multiaddr::empty() -/// .with(Protocol::Memory(40)) // Relay address. -/// .with(Protocol::P2p(PeerId::random().into())) // Relay peer id. -/// .with(Protocol::P2pCircuit) // Signal to connect via relay and not directly. -/// .with(Protocol::Memory(42)) // Destination address. -/// .with(Protocol::P2p(PeerId::random().into())); // Destination peer id. -/// relay_transport.dial(dst_addr_via_relay).unwrap(); -/// ``` -/// -/// 3. Listen for incoming relayed connections via specific relay. -/// -/// ``` -/// # use libp2p_core::{Multiaddr, multiaddr::{Protocol}, PeerId, Transport}; -/// # use libp2p_core::transport::memory::MemoryTransport; -/// # use libp2p_relay::v1::{RelayConfig, new_transport_and_behaviour}; -/// # let inner_transport = MemoryTransport::default(); -/// # let (relay_transport, relay_behaviour) = new_transport_and_behaviour( -/// # RelayConfig::default(), -/// # inner_transport, -/// # ); -/// let relay_addr = Multiaddr::empty() -/// .with(Protocol::Memory(40)) // Relay address. -/// .with(Protocol::P2p(PeerId::random().into())) // Relay peer id. -/// .with(Protocol::P2pCircuit); // Signal to listen via remote relay node. -/// relay_transport.listen_on(relay_addr).unwrap(); -/// ``` -/// -/// 4. Listen for incoming relayed connections via any relay. -/// -/// Note: Without this listener, incoming relayed connections from relays, that the local node is -/// not explicitly listening via, are dropped. -/// -/// ``` -/// # use libp2p_core::{Multiaddr, multiaddr::{Protocol}, PeerId, Transport}; -/// # use libp2p_core::transport::memory::MemoryTransport; -/// # use libp2p_relay::v1::{RelayConfig, new_transport_and_behaviour}; -/// # let inner_transport = MemoryTransport::default(); -/// # let (relay_transport, relay_behaviour) = new_transport_and_behaviour( -/// # RelayConfig::default(), -/// # inner_transport, -/// # ); -/// let addr = Multiaddr::empty() -/// .with(Protocol::P2pCircuit); // Signal to listen via any relay. -/// relay_transport.listen_on(addr).unwrap(); -/// ``` -#[derive(Clone)] -pub struct RelayTransport { - to_behaviour: mpsc::Sender, - - inner_transport: T, -} - -impl RelayTransport { - /// Create a new [`RelayTransport`] by wrapping an existing [`Transport`] in a - /// [`RelayTransport`]. - /// - ///``` - /// # use libp2p_core::transport::dummy::DummyTransport; - /// # use libp2p_relay::v1::{RelayConfig, new_transport_and_behaviour}; - /// - /// let inner_transport = DummyTransport::<()>::new(); - /// let (relay_transport, relay_behaviour) = new_transport_and_behaviour( - /// RelayConfig::default(), - /// inner_transport, - /// ); - ///``` - pub(crate) fn new(t: T) -> (Self, mpsc::Receiver) { - let (to_behaviour, from_transport) = mpsc::channel(0); - - let transport = RelayTransport { - to_behaviour, - - inner_transport: t, - }; - - (transport, from_transport) - } -} - -impl Transport for RelayTransport { - type Output = EitherOutput<::Output, Connection>; - type Error = EitherError<::Error, RelayError>; - type Listener = RelayListener; - type ListenerUpgrade = RelayedListenerUpgrade; - type Dial = EitherFuture<::Dial, RelayedDial>; - - fn listen_on(self, addr: Multiaddr) -> Result> { - let orig_addr = addr.clone(); - - match parse_relayed_multiaddr(addr)? { - // Address does not contain circuit relay protocol. Use inner transport. - Err(addr) => { - let inner_listener = match self.inner_transport.listen_on(addr) { - Ok(listener) => listener, - Err(TransportError::MultiaddrNotSupported(addr)) => { - return Err(TransportError::MultiaddrNotSupported(addr)) - } - Err(TransportError::Other(err)) => { - return Err(TransportError::Other(EitherError::A(err))) - } - }; - Ok(RelayListener::Inner(inner_listener)) - } - // Address does contain circuit relay protocol. Use relayed listener. - Ok(relayed_addr) => { - let relay_peer_id_and_addr = match relayed_addr { - // TODO: In the future we might want to support listening via a relay by its - // address only. - RelayedMultiaddr { - relay_peer_id: None, - relay_addr: Some(_), - .. - } => return Err(RelayError::MissingRelayPeerId.into()), - // TODO: In the future we might want to support listening via a relay by its - // peer_id only. - RelayedMultiaddr { - relay_peer_id: Some(_), - relay_addr: None, - .. - } => return Err(RelayError::MissingRelayAddr.into()), - // Listen for incoming relayed connections via specific relay. - RelayedMultiaddr { - relay_peer_id: Some(peer_id), - relay_addr: Some(addr), - .. - } => Some((peer_id, addr)), - // Listen for incoming relayed connections via any relay. - RelayedMultiaddr { - relay_peer_id: None, - relay_addr: None, - .. - } => None, - }; - - let (to_listener, from_behaviour) = mpsc::channel(0); - let mut to_behaviour = self.to_behaviour; - let msg_to_behaviour = Some( - async move { - to_behaviour - .send(TransportToBehaviourMsg::ListenReq { - relay_peer_id_and_addr, - to_listener, - }) - .await - } - .boxed(), - ); - - Ok(RelayListener::Relayed { - from_behaviour, - msg_to_behaviour, - report_listen_addr: Some(orig_addr), - }) - } - } - } - - fn dial(self, addr: Multiaddr) -> Result> { - self.do_dial(addr, Endpoint::Dialer) - } - - fn dial_as_listener(self, addr: Multiaddr) -> Result> { - self.do_dial(addr, Endpoint::Listener) - } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner_transport.address_translation(server, observed) - } -} - -impl RelayTransport { - fn do_dial( - self, - addr: Multiaddr, - role_override: Endpoint, - ) -> Result<::Dial, TransportError<::Error>> { - match parse_relayed_multiaddr(addr)? { - // Address does not contain circuit relay protocol. Use inner transport. - Err(addr) => { - let dial = match role_override { - Endpoint::Dialer => self.inner_transport.dial(addr), - Endpoint::Listener => self.inner_transport.dial_as_listener(addr), - }; - match dial { - Ok(dialer) => Ok(EitherFuture::First(dialer)), - Err(TransportError::MultiaddrNotSupported(addr)) => { - Err(TransportError::MultiaddrNotSupported(addr)) - } - Err(TransportError::Other(err)) => { - Err(TransportError::Other(EitherError::A(err))) - } - } - } - // Address does contain circuit relay protocol. Dial destination via relay. - Ok(RelayedMultiaddr { - relay_peer_id, - relay_addr, - dst_peer_id, - dst_addr, - }) => { - // TODO: In the future we might want to support dialing a relay by its address only. - let relay_peer_id = relay_peer_id.ok_or(RelayError::MissingRelayPeerId)?; - let relay_addr = relay_addr.ok_or(RelayError::MissingRelayAddr)?; - let dst_peer_id = dst_peer_id.ok_or(RelayError::MissingDstPeerId)?; - - let mut to_behaviour = self.to_behaviour; - Ok(EitherFuture::Second( - async move { - let (tx, rx) = oneshot::channel(); - to_behaviour - .send(TransportToBehaviourMsg::DialReq { - request_id: RequestId::new(), - relay_addr, - relay_peer_id, - dst_addr, - dst_peer_id, - send_back: tx, - }) - .await?; - let stream = rx.await??; - Ok(stream) - } - .boxed(), - )) - } - } - } -} - -#[derive(Default)] -struct RelayedMultiaddr { - relay_peer_id: Option, - relay_addr: Option, - dst_peer_id: Option, - dst_addr: Option, -} - -/// Parse a [`Multiaddr`] containing a [`Protocol::P2pCircuit`]. -/// -/// Returns `Ok(Err(provided_addr))` when passed address contains no [`Protocol::P2pCircuit`]. -/// -/// Returns `Err(_)` when address is malformed. -fn parse_relayed_multiaddr( - addr: Multiaddr, -) -> Result, RelayError> { - if !addr.iter().any(|p| matches!(p, Protocol::P2pCircuit)) { - return Ok(Err(addr)); - } - - let mut relayed_multiaddr = RelayedMultiaddr::default(); - - let mut before_circuit = true; - for protocol in addr.into_iter() { - match protocol { - Protocol::P2pCircuit => { - if before_circuit { - before_circuit = false; - } else { - return Err(RelayError::MultipleCircuitRelayProtocolsUnsupported); - } - } - Protocol::P2p(hash) => { - let peer_id = PeerId::from_multihash(hash).map_err(|_| RelayError::InvalidHash)?; - - if before_circuit { - if relayed_multiaddr.relay_peer_id.is_some() { - return Err(RelayError::MalformedMultiaddr); - } - relayed_multiaddr.relay_peer_id = Some(peer_id) - } else { - if relayed_multiaddr.dst_peer_id.is_some() { - return Err(RelayError::MalformedMultiaddr); - } - relayed_multiaddr.dst_peer_id = Some(peer_id) - } - } - p => { - if before_circuit { - relayed_multiaddr - .relay_addr - .get_or_insert(Multiaddr::empty()) - .push(p); - } else { - relayed_multiaddr - .dst_addr - .get_or_insert(Multiaddr::empty()) - .push(p); - } - } - } - } - - Ok(Ok(relayed_multiaddr)) -} - -#[pin_project(project = RelayListenerProj)] -pub enum RelayListener { - Inner(#[pin] ::Listener), - Relayed { - from_behaviour: mpsc::Receiver, - - msg_to_behaviour: Option>>, - report_listen_addr: Option, - }, -} - -impl Stream for RelayListener { - type Item = Result< - ListenerEvent, EitherError<::Error, RelayError>>, - EitherError<::Error, RelayError>, - >; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = self.project(); - - match this { - RelayListenerProj::Inner(listener) => match listener.poll_next(cx) { - Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(EitherError::A(e)))), - Poll::Ready(Some(Ok(ListenerEvent::Upgrade { - upgrade, - local_addr, - remote_addr, - }))) => { - return Poll::Ready(Some(Ok(ListenerEvent::Upgrade { - upgrade: RelayedListenerUpgrade::Inner(upgrade), - local_addr, - remote_addr, - }))) - } - Poll::Ready(Some(Ok(ListenerEvent::NewAddress(addr)))) => { - return Poll::Ready(Some(Ok(ListenerEvent::NewAddress(addr)))) - } - Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(addr)))) => { - return Poll::Ready(Some(Ok(ListenerEvent::AddressExpired(addr)))) - } - Poll::Ready(Some(Ok(ListenerEvent::Error(err)))) => { - return Poll::Ready(Some(Ok(ListenerEvent::Error(EitherError::A(err))))) - } - Poll::Ready(None) => return Poll::Ready(None), - Poll::Pending => {} - }, - RelayListenerProj::Relayed { - from_behaviour, - msg_to_behaviour, - report_listen_addr, - } => { - if let Some(msg) = msg_to_behaviour { - match Future::poll(msg.as_mut(), cx) { - Poll::Ready(Ok(())) => *msg_to_behaviour = None, - Poll::Ready(Err(e)) => { - return Poll::Ready(Some(Err(EitherError::B(e.into())))) - } - Poll::Pending => {} - } - } - - match from_behaviour.poll_next_unpin(cx) { - Poll::Ready(Some(BehaviourToListenerMsg::IncomingRelayedConnection { - stream, - src_peer_id, - relay_addr, - relay_peer_id: _, - })) => { - return Poll::Ready(Some(Ok(ListenerEvent::Upgrade { - upgrade: RelayedListenerUpgrade::Relayed(Some(stream)), - local_addr: relay_addr.with(Protocol::P2pCircuit), - remote_addr: Protocol::P2p(src_peer_id.into()).into(), - }))); - } - Poll::Ready(Some(BehaviourToListenerMsg::ConnectionToRelayEstablished)) => { - return Poll::Ready(Some(Ok(ListenerEvent::NewAddress( - report_listen_addr - .take() - .expect("ConnectionToRelayEstablished to be send at most once"), - )))); - } - Poll::Ready(None) => return Poll::Ready(None), - Poll::Pending => {} - } - } - } - - Poll::Pending - } -} - -pub type RelayedDial = BoxFuture<'static, Result>; - -#[pin_project(project = RelayedListenerUpgradeProj)] -pub enum RelayedListenerUpgrade { - Inner(#[pin] ::ListenerUpgrade), - Relayed(Option), -} - -impl Future for RelayedListenerUpgrade { - type Output = Result< - EitherOutput<::Output, Connection>, - EitherError<::Error, RelayError>, - >; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.project() { - RelayedListenerUpgradeProj::Inner(upgrade) => match upgrade.poll(cx) { - Poll::Ready(Ok(out)) => return Poll::Ready(Ok(EitherOutput::First(out))), - Poll::Ready(Err(err)) => return Poll::Ready(Err(EitherError::A(err))), - Poll::Pending => {} - }, - RelayedListenerUpgradeProj::Relayed(substream) => { - return Poll::Ready(Ok(EitherOutput::Second( - substream.take().expect("Future polled after completion."), - ))) - } - } - - Poll::Pending - } -} - -/// Error that occurred during relay connection setup. -#[derive(Debug, Eq, PartialEq)] -pub enum RelayError { - MissingRelayPeerId, - MissingRelayAddr, - MissingDstPeerId, - InvalidHash, - SendingMessageToBehaviour(mpsc::SendError), - ResponseFromBehaviourCanceled, - DialingRelay, - MultipleCircuitRelayProtocolsUnsupported, - MalformedMultiaddr, -} - -impl From for TransportError> { - fn from(error: RelayError) -> Self { - TransportError::Other(EitherError::B(error)) - } -} - -impl From for RelayError { - fn from(error: mpsc::SendError) -> Self { - RelayError::SendingMessageToBehaviour(error) - } -} - -impl From for RelayError { - fn from(_: oneshot::Canceled) -> Self { - RelayError::ResponseFromBehaviourCanceled - } -} - -impl From for RelayError { - fn from(error: OutgoingRelayReqError) -> Self { - match error { - OutgoingRelayReqError::DialingRelay => RelayError::DialingRelay, - } - } -} - -impl std::fmt::Display for RelayError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - RelayError::MissingRelayPeerId => { - write!(f, "Missing relay peer id.") - } - RelayError::MissingRelayAddr => { - write!(f, "Missing relay address.") - } - RelayError::MissingDstPeerId => { - write!(f, "Missing destination peer id.") - } - RelayError::InvalidHash => { - write!(f, "Invalid peer id hash.") - } - RelayError::SendingMessageToBehaviour(e) => { - write!(f, "Failed to send message to relay behaviour: {:?}", e) - } - RelayError::ResponseFromBehaviourCanceled => { - write!(f, "Response from behaviour was canceled") - } - RelayError::DialingRelay => { - write!(f, "Dialing relay failed") - } - RelayError::MultipleCircuitRelayProtocolsUnsupported => { - write!(f, "Address contains multiple circuit relay protocols (`p2p-circuit`) which is not supported.") - } - RelayError::MalformedMultiaddr => { - write!(f, "One of the provided multiaddresses is malformed.") - } - } - } -} - -impl std::error::Error for RelayError {} - -/// Message from the [`RelayTransport`] to the [`Relay`](crate::v1::Relay) -/// [`NetworkBehaviour`](libp2p_swarm::NetworkBehaviour). -pub enum TransportToBehaviourMsg { - /// Dial destination node via relay node. - DialReq { - request_id: RequestId, - relay_addr: Multiaddr, - relay_peer_id: PeerId, - dst_addr: Option, - dst_peer_id: PeerId, - send_back: oneshot::Sender>, - }, - /// Listen for incoming relayed connections via relay node. - ListenReq { - /// [`PeerId`] and [`Multiaddr`] of relay node. - /// - /// When [`None`] listen for connections from any relay node. - relay_peer_id_and_addr: Option<(PeerId, Multiaddr)>, - to_listener: mpsc::Sender, - }, -} - -#[derive(Debug, Eq, PartialEq)] -pub enum OutgoingRelayReqError { - DialingRelay, -} diff --git a/protocols/relay/tests/v1.rs b/protocols/relay/tests/v1.rs deleted file mode 100644 index 1da368f68c2..00000000000 --- a/protocols/relay/tests/v1.rs +++ /dev/null @@ -1,1384 +0,0 @@ -// Copyright 2021 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::executor::LocalPool; -use futures::future::FutureExt; -use futures::stream::{Stream, StreamExt}; -use futures::task::Spawn; -use libp2p::kad::record::store::MemoryStore; -use libp2p::NetworkBehaviour; -use libp2p_core::connection::ConnectedPoint; -use libp2p_core::either::EitherTransport; -use libp2p_core::multiaddr::{Multiaddr, Protocol}; -use libp2p_core::transport::{MemoryTransport, Transport, TransportError}; -use libp2p_core::{identity, upgrade, PeerId}; -use libp2p_identify::{Identify, IdentifyConfig, IdentifyEvent, IdentifyInfo}; -use libp2p_kad::{GetClosestPeersOk, Kademlia, KademliaEvent, QueryResult}; -use libp2p_ping as ping; -use libp2p_plaintext::PlainText2Config; -use libp2p_relay::v1::{new_transport_and_behaviour, Relay, RelayConfig}; -use libp2p_swarm::handler::KeepAlive; -use libp2p_swarm::{ - DialError, DummyBehaviour, NetworkBehaviour, NetworkBehaviourAction, - NetworkBehaviourEventProcess, PollParameters, Swarm, SwarmEvent, -}; -use std::task::{Context, Poll}; -use std::time::Duration; -use void::Void; - -#[test] -fn src_connect_to_dst_listening_via_relay() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut dst_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - - let src_peer_id = *src_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - let relay_peer_id = *relay_swarm.local_peer_id(); - - let relay_addr = Multiaddr::empty().with(Protocol::Memory(rand::random::())); - let dst_listen_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.into())) - .with(Protocol::P2pCircuit); - let dst_addr_via_relay = dst_listen_addr_via_relay - .clone() - .with(Protocol::P2p(dst_peer_id.into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - let dst_listener = dst_swarm - .listen_on(dst_listen_addr_via_relay.clone()) - .unwrap(); - - pool.run_until(async { - // Destination Node dialing Relay. - match dst_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) => assert_eq!(peer_id, relay_peer_id), - e => panic!("{:?}", e), - } - - // Destination Node establishing connection to Relay. - match dst_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } => { - assert_eq!(peer_id, relay_peer_id); - } - e => panic!("{:?}", e), - } - - // Destination Node reporting listen address via relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener => { - assert_eq!(address, dst_listen_addr_via_relay); - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - - let dst = async move { - // Destination Node receiving connection from Source Node via Relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::IncomingConnection { send_back_addr, .. } => { - assert_eq!( - send_back_addr, - Protocol::P2p(src_peer_id.clone().into()).into() - ); - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Destination Node establishing connection from Source Node via Relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == src_peer_id => { - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Destination Node waiting for Ping from Source Node via Relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::ConnectionClosed { peer_id, .. } => { - assert_eq!(peer_id, src_peer_id); - break; - } - e => panic!("{:?}", e), - } - } - }; - - src_swarm.dial(dst_addr_via_relay).unwrap(); - let src = async move { - // Source Node dialing Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node establishing connection to Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node establishing connection to destination node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == dst_peer_id => { - break - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Source Node waiting for Ping from Destination Node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Ping(ping::Event { - peer, - result: Ok(_), - })) => { - if peer == dst_peer_id { - break; - } - } - e => panic!("{:?}", e), - } - } - }; - - futures::future::join(dst, src).await - }); -} - -#[test] -fn src_connect_to_dst_not_listening_via_active_relay() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut dst_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Active); - - let relay_peer_id = *relay_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(dst_addr.into_iter().next().unwrap()) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - dst_swarm.listen_on(dst_addr.clone()).unwrap(); - // Instruct destination node to listen for incoming relayed connections from unknown relay nodes. - dst_swarm.listen_on(Protocol::P2pCircuit.into()).unwrap(); - spawn_swarm_on_pool(&pool, dst_swarm); - - src_swarm.dial(dst_addr_via_relay).unwrap(); - pool.run_until(async move { - // Source Node dialing Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node establishing connection to Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node establishing connection to destination node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == dst_peer_id => { - break - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Source Node waiting for Ping from Destination Node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Ping(ping::Event { - peer, - result: Ok(_), - })) => { - if peer == dst_peer_id { - break; - } - } - e => panic!("{:?}", e), - } - } - }); -} - -#[test] -fn src_connect_to_dst_via_established_connection_to_relay() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut dst_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - - let relay_peer_id = *relay_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - let dst_listener = dst_swarm.listen_on(dst_addr_via_relay.clone()).unwrap(); - // Wait for destination to listen via relay. - pool.run_until(async { - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Dialing(_) => {} - SwarmEvent::ConnectionEstablished { .. } => {} - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener => { - assert_eq!(address, dst_addr_via_relay); - break; - } - e => panic!("{:?}", e), - } - } - }); - spawn_swarm_on_pool(&pool, dst_swarm); - - pool.run_until(async move { - src_swarm.dial(relay_addr).unwrap(); - - // Source Node establishing connection to Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => { - break - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - src_swarm.dial(dst_addr_via_relay).unwrap(); - - // Source Node establishing connection to destination node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == dst_peer_id => { - break - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Source Node waiting for Ping from Destination Node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Ping(ping::Event { - peer, - result: Ok(_), - })) => { - if peer == dst_peer_id { - break; - } - } - e => panic!("{:?}", e), - } - } - }); -} - -#[test] -fn src_try_connect_to_offline_dst() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - - let relay_peer_id = *relay_swarm.local_peer_id(); - let dst_peer_id = PeerId::random(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(dst_addr.into_iter().next().unwrap()) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - src_swarm.dial(dst_addr_via_relay.clone()).unwrap(); - pool.run_until(async move { - // Source Node dialing Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node establishing connection to Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - loop { - match src_swarm.select_next_some().await { - SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(addresses), - peer_id, - } if *addresses.iter().map(|(a, _)| a).next().unwrap() == dst_addr_via_relay => { - assert_eq!(peer_id, Some(dst_peer_id)); - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - }); -} - -#[test] -fn src_try_connect_to_unsupported_dst() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut dst_swarm = build_keep_alive_only_swarm(); - - let relay_peer_id = *relay_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(dst_addr.into_iter().next().unwrap()) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - dst_swarm.listen_on(dst_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, dst_swarm); - - src_swarm.dial(dst_addr_via_relay.clone()).unwrap(); - pool.run_until(async move { - // Source Node dialing Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node establishing connection to Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - loop { - match src_swarm.select_next_some().await { - SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(addresses), - peer_id, - } if *addresses.iter().map(|(a, _)| a).next().unwrap() == dst_addr_via_relay => { - assert_eq!(peer_id, Some(dst_peer_id)); - break; - } - SwarmEvent::ConnectionClosed { peer_id, .. } if peer_id == relay_peer_id => {} - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - }); -} - -#[test] -fn src_try_connect_to_offline_dst_via_offline_relay() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - - let relay_peer_id = PeerId::random(); - let dst_peer_id = PeerId::random(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(dst_addr.into_iter().next().unwrap()) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - src_swarm.dial(dst_addr_via_relay.clone()).unwrap(); - pool.run_until(async move { - // Source Node dialing Relay to connect to Destination Node. - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - e => panic!("{:?}", e), - } - - // Source Node fail to reach Relay. - match src_swarm.select_next_some().await { - SwarmEvent::OutgoingConnectionError { peer_id, .. } - if peer_id == Some(relay_peer_id) => {} - e => panic!("{:?}", e), - } - - // Source Node fail to reach Destination Node due to failure reaching Relay. - match src_swarm.select_next_some().await { - SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(addresses), - peer_id, - } if *addresses.iter().map(|(a, _)| a).next().unwrap() == dst_addr_via_relay => { - assert_eq!(peer_id, Some(dst_peer_id)); - } - e => panic!("{:?}", e), - } - }); -} - -#[test] -fn firewalled_src_discover_firewalled_dst_via_kad_and_connect_to_dst_via_routable_relay() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut dst_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - - let src_peer_id = *src_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - let relay_peer_id = *relay_swarm.local_peer_id(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.into())) - .with(Protocol::P2pCircuit) - .with(Protocol::P2p(dst_peer_id.into())); - - src_swarm - .behaviour_mut() - .kad - .add_address(&relay_peer_id, relay_addr.clone()); - dst_swarm - .behaviour_mut() - .kad - .add_address(&relay_peer_id, relay_addr.clone()); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - // Destination Node listen via Relay. - let dst_listener = dst_swarm.listen_on(dst_addr_via_relay.clone()).unwrap(); - - pool.run_until(async { - // Destination Node dialing Relay. - match dst_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) => assert_eq!(peer_id, relay_peer_id), - e => panic!("{:?}", e), - } - - // Destination Node establishing connection to Relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } => { - assert_eq!(peer_id, relay_peer_id); - break; - } - SwarmEvent::Behaviour(CombinedEvent::Kad(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Destination Node reporting listen address via relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener => { - assert_eq!(address, dst_addr_via_relay); - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - - // Destination Node bootstrapping. - let query_id = dst_swarm.behaviour_mut().kad.bootstrap().unwrap(); - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Kad( - KademliaEvent::OutboundQueryCompleted { - id, - result: QueryResult::Bootstrap(Ok(_)), - .. - }, - )) if query_id == id => { - if dst_swarm.behaviour_mut().kad.iter_queries().count() == 0 { - break; - } - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - - let dst = async move { - // Destination Node receiving connection from Source Node via Relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::IncomingConnection { send_back_addr, .. } => { - assert_eq!(send_back_addr, Protocol::P2p(src_peer_id.into()).into()); - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Destination Node establishing connection from Source Node via Relay. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == src_peer_id => { - break; - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - - // Destination Node waiting for Source Node to close connection. - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::ConnectionClosed { peer_id, .. } if peer_id == relay_peer_id => {} - SwarmEvent::ConnectionClosed { peer_id, .. } if peer_id == src_peer_id => { - break; - } - SwarmEvent::Behaviour(CombinedEvent::Kad(_)) => {} - e => panic!("{:?}", e), - } - } - }; - - let src = async move { - // Source Node looking for Destination Node on the Kademlia DHT. - let mut query_id = src_swarm.behaviour_mut().kad.get_closest_peers(dst_peer_id); - // One has to retry multiple times to wait for Relay to receive Identify event from Node - // B. - let mut tries = 0; - - // Source Node establishing connection to Relay and, given that the DHT is small, Node - // B. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } => { - if peer_id == relay_peer_id { - continue; - } else if peer_id == dst_peer_id { - break; - } else { - panic!("Unexpected peer id {:?}", peer_id); - } - } - SwarmEvent::Dialing(peer_id) - if peer_id == relay_peer_id || peer_id == dst_peer_id => {} - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Kad( - KademliaEvent::OutboundQueryCompleted { - id, - result: QueryResult::GetClosestPeers(Ok(GetClosestPeersOk { .. })), - .. - }, - )) if id == query_id => { - tries += 1; - if tries > 300 { - panic!("Too many retries."); - } - - query_id = src_swarm.behaviour_mut().kad.get_closest_peers(dst_peer_id); - } - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - - // Source Node waiting for Ping from Destination Node via Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::Behaviour(CombinedEvent::Ping(ping::Event { - peer, - result: Ok(_), - })) => { - if peer == dst_peer_id { - break; - } - } - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - }; - - futures::future::join(dst, src).await - }); -} - -#[test] -fn inactive_connection_timeout() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_keep_alive_swarm(); - let mut dst_swarm = build_keep_alive_swarm(); - let mut relay_swarm = build_keep_alive_swarm(); - - // Connections only kept alive by Source Node and Destination Node. - *relay_swarm.behaviour_mut().keep_alive.keep_alive_mut() = KeepAlive::No; - - let relay_peer_id = *relay_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - let new_listener = dst_swarm.listen_on(dst_addr_via_relay.clone()).unwrap(); - // Wait for destination to listen via relay. - pool.run_until(async { - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Dialing(_) => {} - SwarmEvent::ConnectionEstablished { .. } => {} - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == new_listener => { - assert_eq!(address, dst_addr_via_relay); - break; - } - e => panic!("{:?}", e), - } - } - }); - spawn_swarm_on_pool(&pool, dst_swarm); - - pool.run_until(async move { - src_swarm.dial(relay_addr).unwrap(); - // Source Node dialing Relay. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) if peer_id == relay_peer_id => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => { - break; - } - e => panic!("{:?}", e), - } - } - - src_swarm.dial(dst_addr_via_relay).unwrap(); - - // Source Node establishing connection to destination node via Relay. - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == dst_peer_id => {} - e => panic!("{:?}", e), - } - - // Relay should notice connection between Source Node and B to be idle. It should then close the - // relayed connection and eventually also close the connection to Source Node given that no - // connections are being relayed on the connection. - loop { - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionClosed { peer_id, .. } => { - if peer_id == relay_peer_id { - break; - } - } - e => panic!("{:?}", e), - } - } - }); -} - -#[test] -fn concurrent_connection_same_relay_same_dst() { - let _ = env_logger::try_init(); - - let mut pool = LocalPool::new(); - - let mut src_swarm = build_swarm(Reachability::Firewalled, RelayMode::Passive); - let mut dst_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut relay_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - - let relay_peer_id = *relay_swarm.local_peer_id(); - let dst_peer_id = *dst_swarm.local_peer_id(); - - let relay_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let dst_addr_via_relay = relay_addr - .clone() - .with(Protocol::P2p(relay_peer_id.clone().into())) - .with(Protocol::P2pCircuit) - .with(Protocol::P2p(dst_peer_id.clone().into())); - - relay_swarm.listen_on(relay_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_swarm); - - let dst_listener = dst_swarm.listen_on(dst_addr_via_relay.clone()).unwrap(); - // Wait for destination to listen via relay. - pool.run_until(async { - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Dialing(_) => {} - SwarmEvent::ConnectionEstablished { .. } => {} - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener => { - assert_eq!(address, dst_addr_via_relay); - break; - } - e => panic!("{:?}", e), - } - } - }); - spawn_swarm_on_pool(&pool, dst_swarm); - - pool.run_until(async move { - src_swarm.dial(dst_addr_via_relay.clone()).unwrap(); - src_swarm.dial(dst_addr_via_relay).unwrap(); - - // Source Node establishing two connections to destination node via Relay. - let mut num_established = 0; - loop { - match src_swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == relay_peer_id => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == dst_peer_id => { - num_established += 1; - if num_established == 2 { - break; - } - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Dialing(peer_id) => { - assert_eq!(peer_id, relay_peer_id); - } - e => panic!("{:?}", e), - } - } - }); -} - -/// Yield incoming connection through listener that listens via the relay node used by the -/// connection. In case the local node does not listen via the specific relay node, but has -/// registered a listener for all remaining incoming relayed connections, yield the connection via -/// that _catch-all_ listener. Drop the connection in all other cases. -/// -/// ## Nodes -/// -/// - Destination node explicitly listening via relay node 1 and 2. -/// -/// - 3 relay nodes -/// -/// - Source node 1 and 2 connecting to destination node via relay 1 and 2 respectively. -/// -/// - Source node 3 connecting to destination node via relay 3, expecting first connection attempt -/// to fail as destination node is not listening for incoming connections from any relay node. -/// Expecting second connection attempt to succeed after destination node registered listener for -/// any incoming relayed connection. -#[test] -fn yield_incoming_connection_through_correct_listener() { - let _ = env_logger::try_init(); - let mut pool = LocalPool::new(); - - let mut dst_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut src_1_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut src_2_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut src_3_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut relay_1_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut relay_2_swarm = build_swarm(Reachability::Routable, RelayMode::Passive); - let mut relay_3_swarm = build_swarm(Reachability::Routable, RelayMode::Active); - - let dst_peer_id = *dst_swarm.local_peer_id(); - let src_1_peer_id = *src_1_swarm.local_peer_id(); - let src_2_peer_id = *src_2_swarm.local_peer_id(); - let src_3_peer_id = *src_3_swarm.local_peer_id(); - let relay_1_peer_id = *relay_1_swarm.local_peer_id(); - let relay_2_peer_id = *relay_2_swarm.local_peer_id(); - let relay_3_peer_id = *relay_3_swarm.local_peer_id(); - - let dst_memory_port = Protocol::Memory(rand::random::()); - let dst_addr = Multiaddr::empty().with(dst_memory_port.clone()); - - let relay_1_addr = Multiaddr::empty().with(Protocol::Memory(rand::random::())); - let relay_1_addr_incl_circuit = relay_1_addr - .clone() - .with(Protocol::P2p(relay_1_peer_id.into())) - .with(Protocol::P2pCircuit); - let dst_addr_via_relay_1 = relay_1_addr_incl_circuit - .clone() - .with(Protocol::P2p(dst_peer_id.into())); - let relay_2_addr = Multiaddr::empty().with(Protocol::Memory(rand::random::())); - let relay_2_addr_incl_circuit = relay_2_addr - .clone() - .with(Protocol::P2p(relay_2_peer_id.into())) - .with(Protocol::P2pCircuit); - let dst_addr_via_relay_2 = relay_2_addr_incl_circuit - .clone() - .with(Protocol::P2p(dst_peer_id.into())); - let relay_3_addr = Multiaddr::empty().with(Protocol::Memory(rand::random::())); - let relay_3_addr_incl_circuit = relay_3_addr - .clone() - .with(Protocol::P2p(relay_3_peer_id.into())) - .with(Protocol::P2pCircuit); - let dst_addr_via_relay_3 = relay_3_addr_incl_circuit - .clone() - .with(dst_memory_port) - .with(Protocol::P2p(dst_peer_id.into())); - - relay_1_swarm.listen_on(relay_1_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_1_swarm); - - relay_2_swarm.listen_on(relay_2_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_2_swarm); - - relay_3_swarm.listen_on(relay_3_addr.clone()).unwrap(); - spawn_swarm_on_pool(&pool, relay_3_swarm); - - let dst_listener_via_relay_1 = dst_swarm - .listen_on(relay_1_addr_incl_circuit.clone()) - .unwrap(); - let dst_listener_via_relay_2 = dst_swarm - .listen_on(relay_2_addr_incl_circuit.clone()) - .unwrap(); - // Listen on own address in order for relay 3 to be able to connect to destination node. - let dst_listener = dst_swarm.listen_on(dst_addr.clone()).unwrap(); - - // Wait for destination node to establish connections to relay 1 and 2. - pool.run_until(async { - let mut established = 0u8; - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::Dialing(peer_id) - if peer_id == relay_1_peer_id || peer_id == relay_2_peer_id => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } - if peer_id == relay_1_peer_id || peer_id == relay_2_peer_id => - { - established += 1; - if established == 2 { - break; - } - } - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener_via_relay_2 => { - assert_eq!(address, relay_2_addr_incl_circuit) - } - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener_via_relay_1 => { - assert_eq!(address, relay_1_addr_incl_circuit) - } - SwarmEvent::NewListenAddr { - address, - listener_id, - } if listener_id == dst_listener => assert_eq!(address, dst_addr), - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - }); - - src_1_swarm.dial(dst_addr_via_relay_1.clone()).unwrap(); - src_2_swarm.dial(dst_addr_via_relay_2.clone()).unwrap(); - spawn_swarm_on_pool(&pool, src_1_swarm); - spawn_swarm_on_pool(&pool, src_2_swarm); - - // Expect source node 1 and 2 to reach destination node via relay 1 and 2 respectively. - pool.run_until(async { - let mut src_1_established = false; - let mut src_2_established = false; - let mut src_1_ping = false; - let mut src_2_ping = false; - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::IncomingConnection { .. } => {} - SwarmEvent::ConnectionEstablished { - peer_id, endpoint, .. - } => { - let local_addr = match endpoint { - ConnectedPoint::Dialer { .. } => unreachable!(), - ConnectedPoint::Listener { local_addr, .. } => local_addr, - }; - - if peer_id == src_1_peer_id { - assert_eq!(local_addr, relay_1_addr_incl_circuit); - src_1_established = true; - } else if peer_id == src_2_peer_id { - assert_eq!(local_addr, relay_2_addr_incl_circuit); - src_2_established = true; - } else { - unreachable!(); - } - } - SwarmEvent::NewListenAddr { address, .. } - if address == relay_1_addr_incl_circuit - || address == relay_2_addr_incl_circuit - || address == dst_addr => {} - SwarmEvent::Behaviour(CombinedEvent::Ping(ping::Event { - peer, - result: Ok(_), - })) => { - if peer == src_1_peer_id { - src_1_ping = true; - } else if peer == src_2_peer_id { - src_2_ping = true; - } - } - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - - if src_1_established && src_2_established && src_1_ping && src_2_ping { - break; - } - } - }); - - // Expect destination node to reject incoming connection from unknown relay given that - // destination node is not listening for such connections. - src_3_swarm.dial(dst_addr_via_relay_3.clone()).unwrap(); - pool.run_until(async { - loop { - futures::select! { - event = dst_swarm.select_next_some() => match event { - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::IncomingConnection { .. } => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } - if peer_id == relay_3_peer_id => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } - if peer_id == src_3_peer_id => - { - panic!( - "Expected destination node to reject incoming connection from unknown relay \ - without a catch-all listener", - ); - } - e => panic!("{:?}", e), - }, - event = src_3_swarm.select_next_some() => match event { - SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(addresses), - peer_id, - } if *addresses.iter().map(|(a, _)| a).next().unwrap() - == dst_addr_via_relay_3 => - { - assert_eq!(peer_id, Some(dst_peer_id)); - break; - } - SwarmEvent::Dialing { .. } => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } - if peer_id == relay_3_peer_id => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } - if peer_id == dst_peer_id => - { - panic!( - "Expected destination node to reject incoming connection from unknown relay \ - without a catch-all listener", - ); - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - e => panic!("{:?}", e), - } - } - } - }); - - // Instruct destination node to listen for incoming relayed connections from unknown relay nodes. - dst_swarm.listen_on(Protocol::P2pCircuit.into()).unwrap(); - // Wait for destination node to report new listen address. - pool.run_until(async { - loop { - match dst_swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } - if address == Protocol::P2pCircuit.into() => - { - break - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - }); - spawn_swarm_on_pool(&pool, dst_swarm); - - // Expect destination node to accept incoming connection from "unknown" relay, i.e. the - // connection from source node 3 via relay 3. - src_3_swarm.dial(dst_addr_via_relay_3.clone()).unwrap(); - pool.run_until(async move { - loop { - match src_3_swarm.select_next_some().await { - SwarmEvent::Dialing(_) => {} - SwarmEvent::ConnectionEstablished { peer_id, .. } if peer_id == dst_peer_id => { - break - } - SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} - SwarmEvent::Behaviour(CombinedEvent::Kad(KademliaEvent::RoutingUpdated { - .. - })) => {} - e => panic!("{:?}", e), - } - } - }); -} - -#[derive(NetworkBehaviour)] -#[behaviour( - out_event = "CombinedEvent", - poll_method = "poll", - event_process = true -)] -struct CombinedBehaviour { - relay: Relay, - ping: ping::Behaviour, - kad: Kademlia, - identify: Identify, - - #[behaviour(ignore)] - events: Vec, -} - -#[derive(Debug)] -enum CombinedEvent { - Kad(KademliaEvent), - Ping(ping::Event), -} - -impl CombinedBehaviour { - fn poll( - &mut self, - _: &mut Context, - _: &mut impl PollParameters, - ) -> Poll::ConnectionHandler>> - { - if !self.events.is_empty() { - return Poll::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))); - } - - Poll::Pending - } -} - -impl NetworkBehaviourEventProcess for CombinedBehaviour { - fn inject_event(&mut self, event: ping::Event) { - self.events.push(CombinedEvent::Ping(event)); - } -} - -impl NetworkBehaviourEventProcess for CombinedBehaviour { - fn inject_event(&mut self, event: KademliaEvent) { - self.events.push(CombinedEvent::Kad(event)); - } -} - -impl NetworkBehaviourEventProcess for CombinedBehaviour { - fn inject_event(&mut self, event: IdentifyEvent) { - if let IdentifyEvent::Received { - peer_id, - info: IdentifyInfo { listen_addrs, .. }, - .. - } = event - { - for addr in listen_addrs { - self.kad.add_address(&peer_id, addr); - } - } - } -} - -impl NetworkBehaviourEventProcess<()> for CombinedBehaviour { - fn inject_event(&mut self, _event: ()) { - unreachable!(); - } -} - -#[derive(NetworkBehaviour)] -#[behaviour(event_process = true)] -struct CombinedKeepAliveBehaviour { - relay: Relay, - keep_alive: DummyBehaviour, -} - -impl NetworkBehaviourEventProcess<()> for CombinedKeepAliveBehaviour { - fn inject_event(&mut self, _event: ()) { - unreachable!(); - } -} - -impl NetworkBehaviourEventProcess for CombinedKeepAliveBehaviour { - fn inject_event(&mut self, _event: Void) { - unreachable!(); - } -} - -enum Reachability { - Firewalled, - Routable, -} - -enum RelayMode { - Active, - Passive, -} - -impl From for bool { - fn from(relay: RelayMode) -> Self { - match relay { - RelayMode::Active => true, - RelayMode::Passive => false, - } - } -} - -/// Wrapper around a [`Transport`] allowing outgoing but denying incoming connections. -/// -/// Meant for testing purposes only. -#[derive(Clone)] -pub struct Firewall(pub T); - -impl Transport for Firewall { - type Output = ::Output; - type Error = ::Error; - type Listener = futures::stream::Pending<<::Listener as Stream>::Item>; - type ListenerUpgrade = ::ListenerUpgrade; - type Dial = ::Dial; - - fn listen_on(self, _: Multiaddr) -> Result> { - Ok(futures::stream::pending()) - } - - fn dial(self, addr: Multiaddr) -> Result> { - self.0.dial(addr) - } - - fn dial_as_listener(self, addr: Multiaddr) -> Result> { - self.0.dial_as_listener(addr) - } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.0.address_translation(server, observed) - } -} - -fn build_swarm(reachability: Reachability, relay_mode: RelayMode) -> Swarm { - let local_key = identity::Keypair::generate_ed25519(); - let local_public_key = local_key.public(); - let plaintext = PlainText2Config { - local_public_key: local_public_key.clone(), - }; - let local_peer_id = local_public_key.to_peer_id(); - - let transport = MemoryTransport::default(); - - let transport = match reachability { - Reachability::Firewalled => EitherTransport::Left(Firewall(transport)), - Reachability::Routable => EitherTransport::Right(transport), - }; - - let (transport, relay_behaviour) = new_transport_and_behaviour( - RelayConfig { - actively_connect_to_dst_nodes: relay_mode.into(), - ..Default::default() - }, - transport, - ); - - let transport = transport - .upgrade(upgrade::Version::V1) - .authenticate(plaintext) - .multiplex(libp2p_yamux::YamuxConfig::default()) - .boxed(); - - let combined_behaviour = CombinedBehaviour { - relay: relay_behaviour, - ping: ping::Behaviour::new(ping::Config::new().with_interval(Duration::from_millis(100))), - kad: Kademlia::new( - local_peer_id.clone(), - MemoryStore::new(local_peer_id.clone()), - ), - identify: Identify::new(IdentifyConfig::new( - "test".to_string(), - local_public_key.clone(), - )), - events: Default::default(), - }; - - Swarm::new(transport, combined_behaviour, local_peer_id) -} - -fn build_keep_alive_swarm() -> Swarm { - let local_key = identity::Keypair::generate_ed25519(); - let local_public_key = local_key.public(); - let plaintext = PlainText2Config { - local_public_key: local_public_key.clone(), - }; - let local_peer_id = local_public_key.to_peer_id(); - - let transport = MemoryTransport::default(); - - let (transport, relay_behaviour) = - new_transport_and_behaviour(RelayConfig::default(), transport); - - let transport = transport - .upgrade(upgrade::Version::V1) - .authenticate(plaintext) - .multiplex(libp2p_yamux::YamuxConfig::default()) - .boxed(); - - let combined_behaviour = CombinedKeepAliveBehaviour { - relay: relay_behaviour, - keep_alive: DummyBehaviour::with_keep_alive(KeepAlive::Yes), - }; - - Swarm::new(transport, combined_behaviour, local_peer_id) -} - -fn build_keep_alive_only_swarm() -> Swarm { - let local_key = identity::Keypair::generate_ed25519(); - let local_public_key = local_key.public(); - let plaintext = PlainText2Config { - local_public_key: local_public_key.clone(), - }; - let local_peer_id = local_public_key.to_peer_id(); - - let transport = MemoryTransport::default(); - - let transport = transport - .upgrade(upgrade::Version::V1) - .authenticate(plaintext) - .multiplex(libp2p_yamux::YamuxConfig::default()) - .boxed(); - - Swarm::new( - transport, - DummyBehaviour::with_keep_alive(KeepAlive::Yes), - local_peer_id, - ) -} - -fn spawn_swarm_on_pool(pool: &LocalPool, mut swarm: Swarm) { - pool.spawner() - .spawn_obj( - async move { - loop { - swarm.next().await; - } - } - .boxed() - .into(), - ) - .unwrap(); -}