From 6cc5d7cb5913f0828828b0cc31ca9ea15bea8c7c Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:05:54 +0100 Subject: [PATCH 01/97] Implement the request response part of AutoNATv2 --- Cargo.lock | 14 + Cargo.toml | 1 + protocols/autonatv2/Cargo.toml | 24 ++ protocols/autonatv2/src/dial_back.rs | 0 protocols/autonatv2/src/generated/mod.rs | 2 + .../autonatv2/src/generated/structs.proto | 57 +++ protocols/autonatv2/src/generated/structs.rs | 338 ++++++++++++++++++ protocols/autonatv2/src/lib.rs | 18 + protocols/autonatv2/src/request_response.rs | 309 ++++++++++++++++ 9 files changed, 763 insertions(+) create mode 100644 protocols/autonatv2/Cargo.toml create mode 100644 protocols/autonatv2/src/dial_back.rs create mode 100644 protocols/autonatv2/src/generated/mod.rs create mode 100644 protocols/autonatv2/src/generated/structs.proto create mode 100644 protocols/autonatv2/src/generated/structs.rs create mode 100644 protocols/autonatv2/src/lib.rs create mode 100644 protocols/autonatv2/src/request_response.rs diff --git a/Cargo.lock b/Cargo.lock index 0172af429ee..e04b81e11d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2443,6 +2443,20 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "libp2p-autonatv2" +version = "0.1.0" +dependencies = [ + "async-trait", + "futures", + "libp2p-core", + "libp2p-request-response", + "libp2p-swarm", + "quick-protobuf", + "rand 0.8.5", + "rand_core 0.6.4", +] + [[package]] name = "libp2p-connection-limits" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 2c823756bbe..fd3857ab685 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "muxers/test-harness", "muxers/yamux", "protocols/autonat", + "protocols/autonatv2", "protocols/dcutr", "protocols/floodsub", "protocols/gossipsub", diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml new file mode 100644 index 00000000000..319bab8e83e --- /dev/null +++ b/protocols/autonatv2/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "libp2p-autonatv2" +version = "0.1.0" +edition = "2021" +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +quick-protobuf = "0.8" +libp2p-request-response = { path = "../request-response" } +libp2p-core = { path = "../../core" } +async-trait = "0.1" +futures = "0.3" +rand_core = "0.6" +rand = { version = "0.8", optional = true } +libp2p-swarm = { version = "0.44.0", path = "../../swarm" } + +[lints] +workspace = true + +[features] +default = ["rand"] +rand = ["dep:rand"] diff --git a/protocols/autonatv2/src/dial_back.rs b/protocols/autonatv2/src/dial_back.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/protocols/autonatv2/src/generated/mod.rs b/protocols/autonatv2/src/generated/mod.rs new file mode 100644 index 00000000000..74078a88020 --- /dev/null +++ b/protocols/autonatv2/src/generated/mod.rs @@ -0,0 +1,2 @@ +// Automatically generated mod.rs +pub(crate) mod structs; diff --git a/protocols/autonatv2/src/generated/structs.proto b/protocols/autonatv2/src/generated/structs.proto new file mode 100644 index 00000000000..46f5f38f1b6 --- /dev/null +++ b/protocols/autonatv2/src/generated/structs.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +package structs; + + +message Message { + oneof msg { + DialRequest dialRequest = 1; + DialResponse dialResponse = 2; + DialDataRequest dialDataRequest = 3; + DialDataResponse dialDataResponse = 4; + } +} + + +message DialRequest { + repeated bytes addrs = 1; + fixed64 nonce = 2; +} + + +message DialDataRequest { + uint32 addrIdx = 1; + uint64 numBytes = 2; +} + + +enum DialStatus { + UNUSED = 0; + E_DIAL_ERROR = 100; + E_DIAL_BACK_ERROR = 101; + OK = 200; +} + + +message DialResponse { + enum ResponseStatus { + E_INTERNAL_ERROR = 0; + E_REQUEST_REJECTED = 100; + E_DIAL_REFUSED = 101; + OK = 200; + } + + ResponseStatus status = 1; + uint32 addrIdx = 2; + DialStatus dialStatus = 3; +} + + +message DialDataResponse { + bytes data = 1; +} + + +message DialBack { + fixed64 nonce = 1; +} diff --git a/protocols/autonatv2/src/generated/structs.rs b/protocols/autonatv2/src/generated/structs.rs new file mode 100644 index 00000000000..3fd8956178c --- /dev/null +++ b/protocols/autonatv2/src/generated/structs.rs @@ -0,0 +1,338 @@ +// Automatically generated rust module for 'structs.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use std::borrow::Cow; +use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::*; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum DialStatus { + UNUSED = 0, + E_DIAL_ERROR = 100, + E_DIAL_BACK_ERROR = 101, + OK = 200, +} + +impl Default for DialStatus { + fn default() -> Self { + DialStatus::UNUSED + } +} + +impl From for DialStatus { + fn from(i: i32) -> Self { + match i { + 0 => DialStatus::UNUSED, + 100 => DialStatus::E_DIAL_ERROR, + 101 => DialStatus::E_DIAL_BACK_ERROR, + 200 => DialStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for DialStatus { + fn from(s: &'a str) -> Self { + match s { + "UNUSED" => DialStatus::UNUSED, + "E_DIAL_ERROR" => DialStatus::E_DIAL_ERROR, + "E_DIAL_BACK_ERROR" => DialStatus::E_DIAL_BACK_ERROR, + "OK" => DialStatus::OK, + _ => Self::default(), + } + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct Message<'a> { + pub(crate) msg: structs::mod_Message::OneOfmsg<'a>, +} + +impl<'a> MessageRead<'a> for Message<'a> { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.msg = structs::mod_Message::OneOfmsg::dialRequest(r.read_message::(bytes)?), + Ok(18) => msg.msg = structs::mod_Message::OneOfmsg::dialResponse(r.read_message::(bytes)?), + Ok(26) => msg.msg = structs::mod_Message::OneOfmsg::dialDataRequest(r.read_message::(bytes)?), + Ok(34) => msg.msg = structs::mod_Message::OneOfmsg::dialDataResponse(r.read_message::(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl<'a> MessageWrite for Message<'a> { + fn get_size(&self) -> usize { + 0 + + match self.msg { + structs::mod_Message::OneOfmsg::dialRequest(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialResponse(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::None => 0, + } } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + match self.msg { structs::mod_Message::OneOfmsg::dialRequest(ref m) => { w.write_with_tag(10, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialResponse(ref m) => { w.write_with_tag(18, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => { w.write_with_tag(26, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => { w.write_with_tag(34, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::None => {}, + } Ok(()) + } +} + +pub(crate) mod mod_Message { + +use super::*; + +#[derive(Debug, PartialEq, Clone)] +pub(crate) enum OneOfmsg<'a> { + dialRequest(structs::DialRequest<'a>), + dialResponse(structs::DialResponse), + dialDataRequest(structs::DialDataRequest), + dialDataResponse(structs::DialDataResponse<'a>), + None, +} + +impl<'a> Default for OneOfmsg<'a> { + fn default() -> Self { + OneOfmsg::None + } +} + +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialRequest<'a> { + pub(crate) addrs: Vec>, + pub(crate) nonce: Option, +} + +impl<'a> MessageRead<'a> for DialRequest<'a> { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.addrs.push(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl<'a> MessageWrite for DialRequest<'a> { + fn get_size(&self) -> usize { + 0 + + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() + + self.nonce.as_ref().map_or(0, |_| 1 + 8) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + for s in &self.addrs { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + if let Some(ref s) = self.nonce { w.write_with_tag(17, |w| w.write_fixed64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialDataRequest { + pub(crate) addrIdx: Option, + pub(crate) numBytes: Option, +} + +impl<'a> MessageRead<'a> for DialDataRequest { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.addrIdx = Some(r.read_uint32(bytes)?), + Ok(16) => msg.numBytes = Some(r.read_uint64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialDataRequest { + fn get_size(&self) -> usize { + 0 + + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.numBytes.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.addrIdx { w.write_with_tag(8, |w| w.write_uint32(*s))?; } + if let Some(ref s) = self.numBytes { w.write_with_tag(16, |w| w.write_uint64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialResponse { + pub(crate) status: Option, + pub(crate) addrIdx: Option, + pub(crate) dialStatus: Option, +} + +impl<'a> MessageRead<'a> for DialResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(16) => msg.addrIdx = Some(r.read_uint32(bytes)?), + Ok(24) => msg.dialStatus = Some(r.read_enum(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.dialStatus.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + if let Some(ref s) = self.addrIdx { w.write_with_tag(16, |w| w.write_uint32(*s))?; } + if let Some(ref s) = self.dialStatus { w.write_with_tag(24, |w| w.write_enum(*s as i32))?; } + Ok(()) + } +} + +pub(crate) mod mod_DialResponse { + + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum ResponseStatus { + E_INTERNAL_ERROR = 0, + E_REQUEST_REJECTED = 100, + E_DIAL_REFUSED = 101, + OK = 200, +} + +impl Default for ResponseStatus { + fn default() -> Self { + ResponseStatus::E_INTERNAL_ERROR + } +} + +impl From for ResponseStatus { + fn from(i: i32) -> Self { + match i { + 0 => ResponseStatus::E_INTERNAL_ERROR, + 100 => ResponseStatus::E_REQUEST_REJECTED, + 101 => ResponseStatus::E_DIAL_REFUSED, + 200 => ResponseStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for ResponseStatus { + fn from(s: &'a str) -> Self { + match s { + "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, + "E_REQUEST_REJECTED" => ResponseStatus::E_REQUEST_REJECTED, + "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, + "OK" => ResponseStatus::OK, + _ => Self::default(), + } + } +} + +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialDataResponse<'a> { + pub(crate) data: Option>, +} + +impl<'a> MessageRead<'a> for DialDataResponse<'a> { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.data = Some(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl<'a> MessageWrite for DialDataResponse<'a> { + fn get_size(&self) -> usize { + 0 + + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.data { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialBack { + pub(crate) nonce: Option, +} + +impl<'a> MessageRead<'a> for DialBack { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(9) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialBack { + fn get_size(&self) -> usize { + 0 + + self.nonce.as_ref().map_or(0, |_| 1 + 8) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.nonce { w.write_with_tag(9, |w| w.write_fixed64(*s))?; } + Ok(()) + } +} + diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs new file mode 100644 index 00000000000..c0825159cf4 --- /dev/null +++ b/protocols/autonatv2/src/lib.rs @@ -0,0 +1,18 @@ +pub(crate) mod request_response; +pub(crate) mod dial_back; +mod generated; + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs new file mode 100644 index 00000000000..9d4a5d26a59 --- /dev/null +++ b/protocols/autonatv2/src/request_response.rs @@ -0,0 +1,309 @@ +use std::{borrow::Cow, io, sync::OnceLock}; + +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use libp2p_core::{ + upgrade::{read_length_prefixed, write_length_prefixed}, + Multiaddr, +}; +use libp2p_request_response::{Behaviour, Codec}; +use libp2p_swarm::{ConnectionId, NetworkBehaviour, StreamProtocol}; +use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; +use rand::Rng; + +use crate::generated::structs as proto; + +const REQUEST_MAX_SIZE: usize = 4104; +const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; +const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; +const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; + +pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-request"); + +macro_rules! new_io_invalid_data_err { + ($msg:expr) => { + io::Error::new(io::ErrorKind::InvalidData, $msg) + }; +} + +macro_rules! check_existence { + ($field:ident) => { + $field.ok_or_else(|| new_io_invalid_data_err!(concat!(stringify!($field), " is missing"))) + }; +} + +#[derive(Debug, Clone)] +pub(crate) enum Request { + Dial(DialRequest), + Data(DialDataResponse), +} + +#[derive(Debug, Clone)] +pub(crate) struct DialRequest { + pub(crate) addrs: Vec, + pub(crate) nonce: u64, +} + +#[derive(Debug, Clone)] +pub(crate) struct DialDataResponse { + pub(crate) data_count: usize, +} + +impl Request { + fn from_bytes(bytes: &[u8]) -> io::Result { + let mut reader = BytesReader::from_bytes(bytes); + let msg = proto::Message::from_reader(&mut reader, bytes) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + match msg.msg { + proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { + let addrs: Vec = addrs + .into_iter() + .map(|e| e.to_vec()) + .map(|e| { + Multiaddr::try_from(e).map_err(|err| { + new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + }) + }) + .collect::, io::Error>>()?; + let nonce = check_existence!(nonce)?; + Ok(Self::Dial(DialRequest { addrs, nonce })) + } + proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { + let data_count = check_existence!(data)?.len(); + Ok(Self::Data(DialDataResponse { data_count })) + } + _ => Err(new_io_invalid_data_err!( + "invalid message type, expected dialRequest or dialDataResponse" + )), + } + } + + fn into_bytes(self) -> Cow<'static, [u8]> { + fn make_message_bytes(request: Request) -> Vec { + let msg = match request { + Request::Dial(DialRequest { addrs, nonce }) => { + let addrs = addrs.iter().map(|e| e.to_vec().into()).collect(); + let nonce = Some(nonce); + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { + addrs, + nonce, + }), + } + } + Request::Data(DialDataResponse { data_count }) => { + assert!( + data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse( + proto::DialDataResponse { + data: Some(Cow::Borrowed(&DATA[..data_count])), + }, + ), + } + } + }; + let mut buf = Vec::with_capacity(msg.get_size()); + let mut writer = Writer::new(&mut buf); + msg.write_message(&mut writer).expect("encoding to succeed"); + buf + } + // little optimization: if the data is exactly 4096 bytes, we can use a static buffer. It is + // likely that this is the case, draining the most performance. + if matches!( + self, + Self::Data(DialDataResponse { + data_count: DATA_FIELD_LEN_UPPER_BOUND + }) + ) { + static CELL: OnceLock> = OnceLock::new(); + CELL.get_or_init(move || make_message_bytes(self)).into() + } else { + make_message_bytes(self).into() + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum Response { + Dial(DialResponse), + Data(DialDataRequest), +} + +#[derive(Debug, Clone)] +pub(crate) struct DialDataRequest { + pub(crate) addr_idx: usize, + pub(crate) num_bytes: usize, +} + +#[derive(Debug, Clone)] +pub(crate) struct DialResponse { + pub(crate) status: proto::mod_DialResponse::ResponseStatus, + pub(crate) addr_idx: usize, + pub(crate) dial_status: proto::DialStatus, +} + +impl Response { + fn from_bytes(bytes: &[u8]) -> std::io::Result { + let mut reader = BytesReader::from_bytes(bytes); + let msg = proto::Message::from_reader(&mut reader, bytes) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + + match msg.msg { + proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { + status, + addrIdx, + dialStatus, + }) => { + let status = check_existence!(status)?; + let addr_idx = check_existence!(addrIdx)? as usize; + let dial_status = check_existence!(dialStatus)?; + Ok(Self::Dial(DialResponse { + status, + addr_idx, + dial_status, + })) + } + proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx, + numBytes, + }) => { + let addr_idx = check_existence!(addrIdx)? as usize; + let num_bytes = check_existence!(numBytes)? as usize; + Ok(Self::Data(DialDataRequest { + addr_idx, + num_bytes, + })) + } + _ => Err(new_io_invalid_data_err!( + "invalid message type, expected dialResponse or dialDataRequest" + )), + } + } + + fn into_bytes(self) -> Vec { + let msg = match self { + Self::Dial(DialResponse { + status, + addr_idx, + dial_status, + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { + status: Some(status), + addrIdx: Some(addr_idx as u32), + dialStatus: Some(dial_status), + }), + }, + Self::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + }, + }; + let mut buf = Vec::with_capacity(msg.get_size()); + let mut writer = Writer::new(&mut buf); + msg.write_message(&mut writer).expect("encoding to succeed"); + buf + } +} + +impl DialDataRequest { + pub(crate) fn from_rng(addr_idx: usize, mut rng: R) -> Self { + let num_bytes = rng.gen_range(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND); + Self { + addr_idx, + num_bytes, + } + } + + #[cfg(any(doc, feature = "rand"))] + pub(crate) fn new(addr_idx: usize) -> Self { + Self::from_rng(addr_idx, rand::thread_rng()) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct AutoNATv2Codec { + observed_addr: Multiaddr, + connection_id: ConnectionId, +} + +#[async_trait::async_trait] +impl Codec for AutoNATv2Codec { + type Protocol = String; + type Request = Request; + type Response = Response; + + async fn read_request( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let bytes = read_length_prefixed(io, REQUEST_MAX_SIZE).await?; + Request::from_bytes(&bytes) + } + + async fn read_response( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let bytes = read_length_prefixed(io, 1024).await?; + Response::from_bytes(&bytes) + } + + async fn write_request( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + write_length_prefixed(io, req.into_bytes()).await?; + io.close().await + } + + async fn write_response( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> std::io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + write_length_prefixed(io, res.into_bytes()).await?; + io.close().await + } +} + +#[cfg(test)] +mod tests { + use crate::generated::structs::{mod_Message::OneOfmsg, DialDataResponse, Message}; + + #[test] + fn message_correct_max_size() { + let message_bytes = quick_protobuf::serialize_into_vec(&Message { + msg: OneOfmsg::dialDataResponse(DialDataResponse { + data: Some(vec![0; 4096].into()), + }), + }) + .unwrap(); + assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); + } +} From 6d44a1e0bf088986eaa20da00a7fcb48903eea72 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:56:52 +0100 Subject: [PATCH 02/97] First steps of implementing the client --- Cargo.lock | 13 +- Cargo.toml | 4 - protocols/autonatv2/Cargo.toml | 13 +- protocols/autonatv2/src/client.rs | 38 ++ protocols/autonatv2/src/client/behaviour.rs | 106 ++++++ protocols/autonatv2/src/client/handler.rs | 395 ++++++++++++++++++++ protocols/autonatv2/src/dial_back.rs | 0 protocols/autonatv2/src/lib.rs | 20 +- protocols/autonatv2/src/request_response.rs | 135 ++++--- 9 files changed, 653 insertions(+), 71 deletions(-) create mode 100644 protocols/autonatv2/src/client.rs create mode 100644 protocols/autonatv2/src/client/behaviour.rs create mode 100644 protocols/autonatv2/src/client/handler.rs delete mode 100644 protocols/autonatv2/src/dial_back.rs diff --git a/Cargo.lock b/Cargo.lock index e04b81e11d8..f649e6d236a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2448,13 +2448,18 @@ name = "libp2p-autonatv2" version = "0.1.0" dependencies = [ "async-trait", + "either", "futures", + "futures-bounded", "libp2p-core", - "libp2p-request-response", + "libp2p-identity", "libp2p-swarm", "quick-protobuf", "rand 0.8.5", "rand_core 0.6.4", + "scc", + "thiserror", + "void", ] [[package]] @@ -4872,6 +4877,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scc" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca628bbcc4be16ffaeae429444cf4538d7a23b1aa5457cc9ce9a220286befbc3" + [[package]] name = "schannel" version = "0.1.22" diff --git a/Cargo.toml b/Cargo.toml index fd3857ab685..1eb6ffc392a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,9 +88,7 @@ libp2p-mdns = { version = "0.45.0", path = "protocols/mdns" } libp2p-memory-connection-limits = { version = "0.2.0", path = "misc/memory-connection-limits" } libp2p-metrics = { version = "0.14.0", path = "misc/metrics" } libp2p-mplex = { version = "0.41.0", path = "muxers/mplex" } -libp2p-muxer-test-harness = { path = "muxers/test-harness" } libp2p-noise = { version = "0.44.0", path = "transports/noise" } -libp2p-perf = { version = "0.3.0", path = "protocols/perf" } libp2p-ping = { version = "0.44.0", path = "protocols/ping" } libp2p-plaintext = { version = "0.41.0", path = "transports/plaintext" } libp2p-pnet = { version = "0.24.0", path = "transports/pnet" } @@ -98,10 +96,8 @@ libp2p-quic = { version = "0.10.0", path = "transports/quic" } libp2p-relay = { version = "0.17.0", path = "protocols/relay" } libp2p-rendezvous = { version = "0.14.0", path = "protocols/rendezvous" } libp2p-request-response = { version = "0.26.0", path = "protocols/request-response" } -libp2p-server = { version = "0.12.3", path = "misc/server" } libp2p-swarm = { version = "0.44.0", path = "swarm" } libp2p-swarm-derive = { version = "0.34.0", path = "swarm-derive" } -libp2p-swarm-test = { version = "0.3.0", path = "swarm-test" } libp2p-tcp = { version = "0.41.0", path = "transports/tcp" } libp2p-tls = { version = "0.3.0", path = "transports/tls" } libp2p-uds = { version = "0.40.0", path = "transports/uds" } diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 319bab8e83e..7792cf97239 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -8,13 +8,18 @@ rust-version.workspace = true [dependencies] quick-protobuf = "0.8" -libp2p-request-response = { path = "../request-response" } -libp2p-core = { path = "../../core" } +libp2p-core = { workspace = true } async-trait = "0.1" -futures = "0.3" rand_core = "0.6" rand = { version = "0.8", optional = true } -libp2p-swarm = { version = "0.44.0", path = "../../swarm" } +libp2p-swarm = { workspace = true } +libp2p-identity = { workspace = true } +futures-bounded = { workspace = true } +void = "1.0.2" +either = "1.9.0" +futures = "0.3.29" +thiserror = "1.0.50" +scc = "2.0.3" [lints] workspace = true diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs new file mode 100644 index 00000000000..350afcd31cf --- /dev/null +++ b/protocols/autonatv2/src/client.rs @@ -0,0 +1,38 @@ +use libp2p_core::Multiaddr; + +use crate::request_response::{DialRequest, DialResponse}; + +mod behaviour; +mod handler; + +#[derive(Debug)] +enum ToBehaviour { + ResponseInfo(ResponseInfo), + OutboundError(E), +} + +#[derive(Debug)] +struct ResponseInfo { + response: DialResponse, + suspicious_addrs: Vec, + successfull_addr: Option, +} + +impl ResponseInfo { + fn new( + response: DialResponse, + suspicious_addrs: Vec, + successfull_addr: Option, + ) -> Self { + Self { + response, + suspicious_addrs, + successfull_addr, + } + } +} + +#[derive(Debug)] +enum FromBehaviour { + Dial(DialRequest), +} diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs new file mode 100644 index 00000000000..5351453b141 --- /dev/null +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -0,0 +1,106 @@ +use std::{ + collections::{HashSet, VecDeque}, + sync::Arc, + task::{Context, Poll}, +}; + +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, ToSwarm, +}; + +use crate::{ + client::{ResponseInfo, ToBehaviour}, + generated::structs::DialStatus, + request_response::DialResponse, +}; + +use super::handler::Handler; + +enum Command { + TestListenerReachability { + server_peer: PeerId, + local_addrs: Vec, + }, +} + +pub struct ReachabilityTestSucc { + pub server_peer: PeerId, + pub local_addr: Multiaddr, +} + +#[derive(Debug, thiserror::Error)] +pub enum ReachabilityTestError {} + +pub enum Event { + CompletedReachabilityTest(Result), +} + +pub(super) struct Behaviour { + pending_commands: VecDeque, + accepted_nonce: Arc>, +} + +impl NetworkBehaviour for Behaviour { + type ConnectionHandler = Handler; + + type ToSwarm = (); + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result { + Ok(Handler::new(self.accepted_nonce.clone(), peer)) + } + + fn handle_established_outbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result { + Ok(Handler::new(self.accepted_nonce.clone(), peer)) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + todo!() + } + + fn on_connection_handler_event( + &mut self, + _peer_id: PeerId, + _connection_id: ConnectionId, + event: ::ToBehaviour, + ) { + match event { + ToBehaviour::ResponseInfo(ResponseInfo { + response: + DialResponse { + status, + addr_idx, + dial_status, + }, + suspicious_addrs, + successfull_addr, + }) => { + todo!() + } + _ => todo!(), + } + todo!() + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll::FromBehaviour>> + { + todo!() + } +} diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs new file mode 100644 index 00000000000..6db25fe01de --- /dev/null +++ b/protocols/autonatv2/src/client/handler.rs @@ -0,0 +1,395 @@ +use std::{ + cmp::min, + collections::VecDeque, + convert::identity, + i8::MAX, + io, + iter::{once, repeat}, + sync::Arc, + task::Poll, + time::Duration, +}; + +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use libp2p_core::{upgrade::ReadyUpgrade, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use void::Void; + +use crate::{ + client::ToBehaviour, + generated::structs::mod_DialResponse::ResponseStatus, + request_response::{ + DialBack, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + }, + DIAL_BACK_PROTOCOL_NAME, REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, +}; +use crate::{DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND}; + +use super::ResponseInfo; + +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); +const MAX_CONCURRENT_REQUESTS: usize = 10; + +#[derive(Debug, thiserror::Error)] +pub(super) enum Error { + #[error("io error")] + Io(#[from] io::Error), + #[error("invalid data request index: {index} (max: {max})")] + InvalidDataRequestIndex { index: usize, max: usize }, + #[error("data request too large: {len} (max: {max})")] + DataRequestTooLarge { len: usize, max: usize }, + #[error("data request too small: {len} (min: {min})")] + DataRequestTooSmall { len: usize, min: usize }, + #[error("timeout")] + Timeout(#[from] futures_bounded::Timeout), + #[error("invalid nonce: {nonce} (provided by (by))")] + WrongNonceGiven { nonce: u64, by: PeerId }, + #[error("request protocol removed by peer")] + RequestProtocolRemoved, + #[error("dial back protocol removed by me")] + DialBackProtocolRemoved, +} + +pub(super) struct Handler { + /// Queue of events to return when polled. + queued_events: VecDeque< + ConnectionHandlerEvent< + ::OutboundProtocol, + ::OutboundOpenInfo, + ::ToBehaviour, + ::Error, + >, + >, + pending_data: VecDeque, + queued_requests: VecDeque, + inbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet>, + accepted_nonce: Arc>, + remote_peer_id: PeerId, +} + +impl Handler { + pub(super) fn new(accepted_nonce: Arc>, remote_peer_id: PeerId) -> Self { + Self { + queued_events: VecDeque::new(), + pending_data: VecDeque::new(), + queued_requests: VecDeque::new(), + inbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + outbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + accepted_nonce, + remote_peer_id, + } + } +} + +impl ConnectionHandler for Handler { + type Error = Error; + type ToBehaviour = super::ToBehaviour; + type FromBehaviour = super::FromBehaviour; + type InboundProtocol = ReadyUpgrade; + type OutboundProtocol = ReadyUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = Option; + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) + } + + fn poll( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> Poll< + ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::ToBehaviour, + Self::Error, + >, + > { + if let Some(event) = self.queued_events.pop_front() { + return Poll::Ready(event); + } + + if let Some(pending_data) = self.pending_data.pop_front() { + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(REQUEST_UPGRADE, Some(pending_data)), + }); + } + + if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { + let perform_request_res = m.map_err(Error::Timeout).and_then(identity); // necessary until flatten_error stabilized + match perform_request_res { + Ok(PerformRequestStatus::Pending(pending)) => { + self.pending_data.push_back(pending); + } + Ok(PerformRequestStatus::Finished(resp_info)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::ResponseInfo(resp_info), + )) + } + Err(e) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::OutboundError(e), + )); + } + } + } + todo!() + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + match event { + super::FromBehaviour::Dial(dial_req) => { + self.queued_requests.push_back(dial_req); + } + } + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: None, + }) => { + let request = self + .queued_requests + .pop_front() + .expect("opened a stream without a penidng request"); + let request_clone = request.clone(); + if self + .outbound + .try_push(perform_dial_request(protocol, request)) + .is_err() + { + println!( + "Dropping outbound stream because we are at capacity. {request_clone:?}" + ); + self.queued_requests.push_front(request_clone); + } + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: Some(pending_data), + }) => { + let pending_data_copy = pending_data.clone(); + if self + .outbound + .try_push(perform_data_request(protocol, pending_data)) + .is_err() + { + println!("Dropping outbound stream because we are at capacity."); + self.pending_data.push_front(pending_data_copy); + } + } + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + let remote_peer_id = self.remote_peer_id.clone(); + if self + .inbound + .try_push(perform_dial_back( + protocol, + self.accepted_nonce.clone(), + remote_peer_id, + )) + .is_err() + { + println!("Dropping inbound stream because we are at capacity."); + } + } + ConnectionEvent::DialUpgradeError(err) => { + todo!() + } + ConnectionEvent::ListenUpgradeError(err) => { + todo!() + } + ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { + if protocols.any(|e| &DIAL_BACK_PROTOCOL_NAME == e) { + self.queued_events.push_back(ConnectionHandlerEvent::Close( + Error::DialBackProtocolRemoved, + )); + } + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { + if protocols.any(|e| &REQUEST_PROTOCOL_NAME == e) { + self.queued_events + .push_back(ConnectionHandlerEvent::Close(Error::RequestProtocolRemoved)); + } + } + _ => {} + } + todo!() + } +} + +enum PerformRequestStatus { + Finished(ResponseInfo), + Pending(PendingData), +} + +#[derive(Debug, Clone)] +pub(super) struct PendingData { + data_count: usize, + suspicious_addrs: Vec, + all_addrs: Vec, +} + +async fn perform_dial_request( + mut stream: S, + dial_req: DialRequest, +) -> Result +where + S: AsyncRead + AsyncWrite + Unpin, +{ + let addrs = dial_req.addrs.clone(); + let req = Request::Dial(dial_req); + req.write_into(&mut stream).await?; + let resp = Response::read_from(&mut stream).await?; + stream.close().await?; + let DialDataRequest { + addr_idx, + num_bytes, + } = match resp { + Response::Dial(resp) => { + let successfull_addr = if resp.status == ResponseStatus::OK { + addrs.get(resp.addr_idx).cloned() + } else { + None + }; + return Ok(PerformRequestStatus::Finished(ResponseInfo::new( + resp, + Vec::new(), + successfull_addr, + ))); + } + Response::Data(data_req) => data_req, + }; + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(Error::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } else if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); + } + let suspicious_addr = addrs + .get(addr_idx) + .ok_or(Error::InvalidDataRequestIndex { + index: addr_idx, + max: addrs.len(), + })? + .clone(); + Ok(PerformRequestStatus::Pending(PendingData { + data_count: num_bytes, + suspicious_addrs: vec![suspicious_addr], + all_addrs: addrs, + })) +} + +async fn perform_data_request( + mut stream: S, + PendingData { + mut data_count, + mut suspicious_addrs, + all_addrs, + }: PendingData, +) -> Result +where + S: AsyncRead + AsyncWrite + Unpin, +{ + let data_field_len = min(DATA_FIELD_LEN_UPPER_BOUND, data_count); + data_count -= data_field_len; + let req = Request::Data(DialDataResponse { + data_count: data_field_len, + }); + req.write_into(&mut stream).await?; + if data_count != 0 { + return Ok(PerformRequestStatus::Pending(PendingData { + data_count, + suspicious_addrs, + all_addrs, + })); + } + let resp = Response::read_from(&mut stream).await?; + stream.close().await?; + match resp { + Response::Dial(dial_resp) => { + let successfull_addr = if dial_resp.status == ResponseStatus::OK { + all_addrs.get(dial_resp.addr_idx).cloned() + } else { + None + }; + Ok(PerformRequestStatus::Finished(ResponseInfo::new( + dial_resp, + suspicious_addrs, + successfull_addr, + ))) + } + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(Error::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } else if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); + } + let suspicious_addr = + all_addrs + .get(addr_idx) + .ok_or(Error::InvalidDataRequestIndex { + index: addr_idx, + max: all_addrs.len(), + })?; + if !suspicious_addrs.contains(suspicious_addr) { + suspicious_addrs.push(suspicious_addr.clone()); + } + Ok(PerformRequestStatus::Pending(PendingData { + data_count: num_bytes, + suspicious_addrs, + all_addrs, + })) + } + } +} + +async fn perform_dial_back( + mut stream: S, + accepted_nonce: Arc>, + remote_peer_id: PeerId, +) -> Result<(), Error> +where + S: AsyncRead + AsyncWrite + Unpin, +{ + let DialBack { nonce } = DialBack::read_from(&mut stream).await?; + if !accepted_nonce.contains_async(&nonce).await { + return Err(Error::WrongNonceGiven { + nonce, + by: remote_peer_id, + }); + } + stream.close().await?; + Ok(()) +} diff --git a/protocols/autonatv2/src/dial_back.rs b/protocols/autonatv2/src/dial_back.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index c0825159cf4..ad58455ca04 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -1,6 +1,22 @@ -pub(crate) mod request_response; -pub(crate) mod dial_back; +use libp2p_core::upgrade::ReadyUpgrade; +use libp2p_swarm::StreamProtocol; + +mod client; mod generated; +pub(crate) mod request_response; + +pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-request"); +pub(crate) const DIAL_BACK_PROTOCOL_NAME: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-back"); +pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = + ReadyUpgrade::new(REQUEST_PROTOCOL_NAME); +pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = + ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); + +pub(crate) use request_response::DATA_FIELD_LEN_UPPER_BOUND; +pub(crate) use request_response::DATA_LEN_LOWER_BOUND; +pub(crate) use request_response::DATA_LEN_UPPER_BOUND; pub fn add(left: usize, right: usize) -> usize { left + right diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 9d4a5d26a59..db003beed6f 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -5,7 +5,6 @@ use libp2p_core::{ upgrade::{read_length_prefixed, write_length_prefixed}, Multiaddr, }; -use libp2p_request_response::{Behaviour, Codec}; use libp2p_swarm::{ConnectionId, NetworkBehaviour, StreamProtocol}; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use rand::Rng; @@ -13,12 +12,9 @@ use rand::Rng; use crate::generated::structs as proto; const REQUEST_MAX_SIZE: usize = 4104; -const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; -const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; -const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; - -pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = - StreamProtocol::new("/libp2p/autonat/2/dial-request"); +pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; +pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; +pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; macro_rules! new_io_invalid_data_err { ($msg:expr) => { @@ -32,6 +28,30 @@ macro_rules! check_existence { }; } +macro_rules! read_from { + () => { + pub(crate) async fn read_from(mut reader: R) -> io::Result + where + R: AsyncRead + Unpin, + { + let bytes = read_length_prefixed(&mut reader, 1024).await?; + Self::from_bytes(&bytes) + } + }; +} + +macro_rules! write_into { + () => { + pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { + let bytes = self.into_bytes(); + write_length_prefixed(&mut writer, bytes).await + } + }; +} + #[derive(Debug, Clone)] pub(crate) enum Request { Dial(DialRequest), @@ -50,6 +70,8 @@ pub(crate) struct DialDataResponse { } impl Request { + read_from!(); + fn from_bytes(bytes: &[u8]) -> io::Result { let mut reader = BytesReader::from_bytes(bytes); let msg = proto::Message::from_reader(&mut reader, bytes) @@ -78,6 +100,8 @@ impl Request { } } + write_into!(); + fn into_bytes(self) -> Cow<'static, [u8]> { fn make_message_bytes(request: Request) -> Vec { let msg = match request { @@ -147,6 +171,8 @@ pub(crate) struct DialResponse { } impl Response { + read_from!(); + fn from_bytes(bytes: &[u8]) -> std::io::Result { let mut reader = BytesReader::from_bytes(bytes); let msg = proto::Message::from_reader(&mut reader, bytes) @@ -184,6 +210,8 @@ impl Response { } } + write_into!(); + fn into_bytes(self) -> Vec { let msg = match self { Self::Dial(DialResponse { @@ -229,66 +257,36 @@ impl DialDataRequest { } } -#[derive(Debug, Clone)] -pub(crate) struct AutoNATv2Codec { - observed_addr: Multiaddr, - connection_id: ConnectionId, -} +const DIAL_BACK_MAX_SIZE: usize = 10; -#[async_trait::async_trait] -impl Codec for AutoNATv2Codec { - type Protocol = String; - type Request = Request; - type Response = Response; - - async fn read_request( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let bytes = read_length_prefixed(io, REQUEST_MAX_SIZE).await?; - Request::from_bytes(&bytes) - } +pub(crate) struct DialBack { + pub nonce: u64, +} - async fn read_response( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let bytes = read_length_prefixed(io, 1024).await?; - Response::from_bytes(&bytes) - } +impl DialBack { + read_from!(); - async fn write_request( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - req: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - write_length_prefixed(io, req.into_bytes()).await?; - io.close().await + fn from_bytes(bytes: &[u8]) -> io::Result { + let mut reader = BytesReader::from_bytes(bytes); + let proto::DialBack { nonce } = proto::DialBack::from_reader(&mut reader, bytes) + .map_err(|err| new_io_invalid_data_err!(err))?; + let nonce = check_existence!(nonce)?; + Ok(Self { nonce }) } - async fn write_response( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - res: Self::Response, - ) -> std::io::Result<()> + pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> where - T: AsyncWrite + Unpin + Send, + W: AsyncWrite + Unpin, { - write_length_prefixed(io, res.into_bytes()).await?; - io.close().await + let msg = proto::DialBack { + nonce: Some(self.nonce), + }; + let mut buf = [0u8; DIAL_BACK_MAX_SIZE]; + debug_assert!(msg.get_size() <= DIAL_BACK_MAX_SIZE); + let mut msg_writer = Writer::new(&mut buf[..]); + msg.write_message(&mut msg_writer) + .expect("encoding to succeed"); + write_length_prefixed(&mut writer, &buf[..msg.get_size()]).await } } @@ -306,4 +304,21 @@ mod tests { .unwrap(); assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); } + + #[test] + fn dial_back_correct_size() { + let dial_back = super::proto::DialBack { nonce: Some(0) }; + let buf = quick_protobuf::serialize_into_vec(&dial_back).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + + let dial_back_none = super::proto::DialBack { nonce: None }; + let buf = quick_protobuf::serialize_into_vec(&dial_back_none).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + + let dial_back_max_nonce = super::proto::DialBack { + nonce: Some(u64::MAX), + }; + let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + } } From 81cd4429782c6ef408da0421c535966df3366a1a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:23:10 +0100 Subject: [PATCH 03/97] Extract ip-global and implemented lot of behaviour --- Cargo.lock | 6 + Cargo.toml | 2 + core/Cargo.toml | 1 + core/src/transport/global_only.rs | 237 +----------------- core/src/upgrade/ready.rs | 2 +- misc/ip-global/Cargo.toml | 12 + misc/ip-global/src/lib.rs | 255 ++++++++++++++++++++ protocols/autonatv2/Cargo.toml | 3 +- protocols/autonatv2/src/client/behaviour.rs | 168 +++++++++++-- 9 files changed, 428 insertions(+), 258 deletions(-) create mode 100644 misc/ip-global/Cargo.toml create mode 100644 misc/ip-global/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f649e6d236a..90a40a8ac61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,6 +2236,10 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ip-global" +version = "0.1.0" + [[package]] name = "ipconfig" version = "0.3.2" @@ -2451,6 +2455,7 @@ dependencies = [ "either", "futures", "futures-bounded", + "ip-global", "libp2p-core", "libp2p-identity", "libp2p-swarm", @@ -2489,6 +2494,7 @@ dependencies = [ "futures", "futures-timer", "instant", + "ip-global", "libp2p-identity", "libp2p-mplex", "libp2p-noise", diff --git a/Cargo.toml b/Cargo.toml index 1eb6ffc392a..0fddc646e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "misc/allow-block-list", "misc/connection-limits", "misc/futures-bounded", + "misc/ip-global", "misc/keygen", "misc/memory-connection-limits", "misc/metrics", @@ -116,6 +117,7 @@ prometheus-client = "0.22.0" quick-protobuf-codec = { version = "0.2.0", path = "misc/quick-protobuf-codec" } quickcheck = { package = "quickcheck-ext", path = "misc/quickcheck-ext" } rw-stream-sink = { version = "0.4.0", path = "misc/rw-stream-sink" } +ip-global = { version = "0.1.0", path = "misc/ip-global" } [patch.crates-io] diff --git a/core/Cargo.toml b/core/Cargo.toml index 4cbfa827af6..0a0a42ecc2e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,6 +16,7 @@ fnv = "1.0" futures = { version = "0.3.29", features = ["executor", "thread-pool"] } futures-timer = "3" instant = "0.1.12" +ip-global = { workspace = true } libp2p-identity = { workspace = true, features = ["peerid", "ed25519"] } log = "0.4" multiaddr = { workspace = true } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 40e99c6a31b..1b4822f2cea 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -20,8 +20,9 @@ use crate::{ multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent, DialOpts}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; +use ip_global::IpExt; use log::debug; use std::{ pin::Pin, @@ -34,236 +35,6 @@ pub struct Transport { inner: T, } -/// This module contains an implementation of the `is_global` IPv4 address space. -/// -/// Credit for this implementation goes to the Rust standard library team. -/// -/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) -mod ipv4_global { - use std::net::Ipv4Addr; - - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - #[inline] - const fn is_reserved(a: Ipv4Addr) -> bool { - a.octets()[0] & 240 == 240 && !a.is_broadcast() - } - - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - #[inline] - const fn is_benchmarking(a: Ipv4Addr) -> bool { - a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - #[inline] - const fn is_shared(a: Ipv4Addr) -> bool { - a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - #[inline] - const fn is_private(a: Ipv4Addr) -> bool { - match a.octets() { - [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, - [192, 168, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[must_use] - #[inline] - pub(crate) const fn is_global(a: Ipv4Addr) -> bool { - !(a.octets()[0] == 0 // "This network" - || is_private(a) - || is_shared(a) - || a.is_loopback() - || a.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) - || a.is_documentation() - || is_benchmarking(a) - || is_reserved(a) - || a.is_broadcast()) - } -} - -/// This module contains an implementation of the `is_global` IPv6 address space. -/// -/// Credit for this implementation goes to the Rust standard library team. -/// -/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) -mod ipv6_global { - use std::net::Ipv6Addr; - - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - #[inline] - const fn is_unicast_link_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xffc0) == 0xfe80 - } - - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - #[inline] - const fn is_unique_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xfe00) == 0xfc00 - } - - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - #[inline] - const fn is_documentation(a: Ipv6Addr) -> bool { - (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - #[inline] - pub(crate) const fn is_global(a: Ipv6Addr) -> bool { - !(a.is_unspecified() - || a.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || is_documentation(a) - || is_unique_local(a) - || is_unicast_link_local(a)) - } -} - impl Transport { pub fn new(transport: T) -> Self { Transport { inner: transport } @@ -295,14 +66,14 @@ impl crate::Transport for Transport { ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if !ipv4_global::is_global(a) { + if !IpExt::is_global(&a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr, opts) } Some(Protocol::Ip6(a)) => { - if !ipv6_global::is_global(a) { + if !IpExt::is_global(&a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } diff --git a/core/src/upgrade/ready.rs b/core/src/upgrade/ready.rs index 323f1f73f32..7e235902651 100644 --- a/core/src/upgrade/ready.rs +++ b/core/src/upgrade/ready.rs @@ -31,7 +31,7 @@ pub struct ReadyUpgrade

{ } impl

ReadyUpgrade

{ - pub fn new(protocol_name: P) -> Self { + pub const fn new(protocol_name: P) -> Self { Self { protocol_name } } } diff --git a/misc/ip-global/Cargo.toml b/misc/ip-global/Cargo.toml new file mode 100644 index 00000000000..7dfb5c3dd6d --- /dev/null +++ b/misc/ip-global/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ip-global" +version = "0.1.0" +edition = "2021" +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lints] +workspace = true diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs new file mode 100644 index 00000000000..aaf7a6b2a89 --- /dev/null +++ b/misc/ip-global/src/lib.rs @@ -0,0 +1,255 @@ +//! This crate provides extension traits for [`Ipv4Addr`], [`Ipv6Addr`] and [`IpAddr`] that provide methods +//! to check if an address is reserved for special use. +//! +//! This is primarily a polyfill for `ip` feature, which is currently unstable. +//! +//! Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) + + +use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; + +pub trait Ipv4Ext { + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + fn is_reserved(&self) -> bool; + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + fn is_benchmarking(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + fn is_shared(&self) -> bool; + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + fn is_private(&self) -> bool; +} + +impl Ipv4Ext for Ipv4Addr { + #[inline] + fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + #[inline] + fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + #[inline] + fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + #[inline] + fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, + } + } +} + +pub trait Ipv6Ext { + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + fn is_unicast_link_local(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + fn is_unique_local(&self) -> bool; + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + fn is_documentation(&self) -> bool; +} + +impl Ipv6Ext for Ipv6Addr { + #[inline] + fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + #[inline] + fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + #[inline] + fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } +} + +pub trait IpExt { + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. + #[must_use] + fn is_global(&self) -> bool; +} + +impl IpExt for Ipv4Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[inline] + fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || Ipv4Ext::is_shared(self) + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || Ipv4Ext::is_benchmarking(self) + || Ipv4Ext::is_reserved(self) + || self.is_broadcast()) + } +} + +impl IpExt for Ipv6Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[inline] + fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || Ipv6Ext::is_documentation(self) + || Ipv6Ext::is_unique_local(self) + || Ipv6Ext::is_unicast_link_local(self)) + } +} + +impl IpExt for IpAddr { + #[inline] + fn is_global(&self) -> bool { + match self { + Self::V4(v4) => IpExt::is_global(v4), + Self::V6(v6) => IpExt::is_global(v6), + } + } +} diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 7792cf97239..7e3ce4bc73e 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -7,14 +7,15 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = "0.1" quick-protobuf = "0.8" libp2p-core = { workspace = true } -async-trait = "0.1" rand_core = "0.6" rand = { version = "0.8", optional = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } futures-bounded = { workspace = true } +ip-global = { workspace = true } void = "1.0.2" either = "1.9.0" futures = "0.3.29" diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 5351453b141..63eb3888cc2 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -4,19 +4,22 @@ use std::{ task::{Context, Poll}, }; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use ip_global::IpExt; +use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, ToSwarm, + dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, + NetworkBehaviour, NotifyHandler, ToSwarm, }; +use rand_core::RngCore; use crate::{ client::{ResponseInfo, ToBehaviour}, - generated::structs::DialStatus, - request_response::DialResponse, + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + request_response::{DialRequest, DialResponse}, }; -use super::handler::Handler; +use super::{handler::Handler, FromBehaviour}; enum Command { TestListenerReachability { @@ -27,25 +30,58 @@ enum Command { pub struct ReachabilityTestSucc { pub server_peer: PeerId, - pub local_addr: Multiaddr, + pub visible_addr: Option, + pub suspicious_addrs: Vec, } #[derive(Debug, thiserror::Error)] -pub enum ReachabilityTestError {} +pub enum ReachabilityTestErr { + #[error("Server chose not to dial any provided address. Server peer id: {peer_id}")] + ServerChoseNotToDialAnyAddress { peer_id: PeerId }, + #[error("Server rejected dial request. Server peer id: {peer_id}")] + ServerRejectedDialRequest { peer_id: PeerId }, + #[error("Server ran into an internal error. Server peer id: {peer_id}")] + InternalServerError { peer_id: PeerId }, + #[error("Server did not respond correctly to dial request. Server peer id: {peer_id}")] + InvalidResponse { peer_id: PeerId }, + #[error("Server was unable to connect to address: {addr:?}. Server peer id: {peer_id}")] + UnableToConnectOnSelectedAddress { + peer_id: PeerId, + addr: Option, + }, + #[error("Server experienced failure during dial back on address: {addr:?} Server peer id: {peer_id}")] + FailureDuringDialBack { + peer_id: PeerId, + addr: Option, + }, +} pub enum Event { - CompletedReachabilityTest(Result), + CompletedReachabilityTest(Result), } -pub(super) struct Behaviour { +pub(super) struct Behaviour +where + R: RngCore + 'static, +{ pending_commands: VecDeque, + pending_events: VecDeque< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + >, accepted_nonce: Arc>, + rng: R, } -impl NetworkBehaviour for Behaviour { +impl NetworkBehaviour for Behaviour +where + R: RngCore + 'static, +{ type ConnectionHandler = Handler; - type ToSwarm = (); + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -74,22 +110,16 @@ impl NetworkBehaviour for Behaviour { fn on_connection_handler_event( &mut self, - _peer_id: PeerId, + peer_id: PeerId, _connection_id: ConnectionId, event: ::ToBehaviour, ) { match event { - ToBehaviour::ResponseInfo(ResponseInfo { - response: - DialResponse { - status, - addr_idx, - dial_status, - }, - suspicious_addrs, - successfull_addr, - }) => { - todo!() + ToBehaviour::ResponseInfo(response_info) => { + let event = Event::CompletedReachabilityTest( + self.handle_response_info(peer_id, response_info), + ); + self.pending_events.push_back(ToSwarm::GenerateEvent(event)); } _ => todo!(), } @@ -101,6 +131,98 @@ impl NetworkBehaviour for Behaviour { cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + if let Some(command) = self.pending_commands.pop_front() { + self.handle_command(command); + return Poll::Pending; + } todo!() } } + +impl Behaviour +where + R: RngCore, +{ + fn handle_response_info( + &mut self, + peer_id: PeerId, + ResponseInfo { + response: + DialResponse { + status, + dial_status, + .. + }, + suspicious_addrs, + successfull_addr, + }: ResponseInfo, + ) -> Result { + match (status, dial_status) { + (ResponseStatus::E_REQUEST_REJECTED, _) => { + Err(ReachabilityTestErr::ServerRejectedDialRequest { peer_id }) + } + (ResponseStatus::E_DIAL_REFUSED, _) => { + Err(ReachabilityTestErr::ServerChoseNotToDialAnyAddress { peer_id }) + } + (ResponseStatus::E_INTERNAL_ERROR, _) => { + Err(ReachabilityTestErr::InternalServerError { peer_id }) + } + (ResponseStatus::OK, DialStatus::UNUSED) => { + Err(ReachabilityTestErr::InvalidResponse { peer_id }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { + Err(ReachabilityTestErr::UnableToConnectOnSelectedAddress { + peer_id, + addr: successfull_addr, + }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { + Err(ReachabilityTestErr::FailureDuringDialBack { + peer_id, + addr: successfull_addr, + }) + } + (ResponseStatus::OK, DialStatus::OK) => Ok(ReachabilityTestSucc { + server_peer: peer_id, + visible_addr: successfull_addr, + suspicious_addrs, + }), + } + } + + fn handle_command(&mut self, command: Command) { + match command { + Command::TestListenerReachability { + server_peer, + local_addrs, + } => { + let cleaned_local_addrs = local_addrs.iter().filter(|addr| { + !addr.iter().any(|p| match p { + Protocol::Ip4(ip) if !IpExt::is_global(&ip) => true, + Protocol::Ip6(ip) if !IpExt::is_global(&ip) => true, + Protocol::Dns(m) + | Protocol::Dns4(m) + | Protocol::Dns6(m) + | Protocol::Dnsaddr(m) => m == "localhost" || m.ends_with(".local"), + _ => false, + }) + }); + let dial_opts = DialOpts::peer_id(server_peer.clone()).build(); + let dial_event = ToSwarm::Dial { opts: dial_opts }; + self.pending_events.push_back(dial_event); + let dial_request = DialRequest { + addrs: local_addrs, + nonce: self.rng.next_u64(), + }; + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: server_peer, + handler: NotifyHandler::Any, + event: FromBehaviour::Dial(dial_request), + }) + } + } + } +} From c0fd889d4f74363ad576fa1537ee4ea48f19be0b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 18 Nov 2023 17:21:30 +0100 Subject: [PATCH 04/97] Intermediate commit --- Cargo.lock | 4 + protocols/autonatv2/Cargo.toml | 4 + protocols/autonatv2/src/client/behaviour.rs | 30 +-- protocols/autonatv2/src/client/handler.rs | 22 ++- .../autonatv2/src/client/handler/dial_back.rs | 12 ++ .../autonatv2/src/generated/structs.proto | 63 +++---- protocols/autonatv2/src/generated/structs.rs | 38 ++-- protocols/autonatv2/src/request_response.rs | 172 +++++++----------- 8 files changed, 158 insertions(+), 187 deletions(-) create mode 100644 protocols/autonatv2/src/client/handler/dial_back.rs diff --git a/Cargo.lock b/Cargo.lock index 90a40a8ac61..d1f76d9b256 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2452,6 +2452,8 @@ name = "libp2p-autonatv2" version = "0.1.0" dependencies = [ "async-trait", + "asynchronous-codec 0.6.2", + "bytes", "either", "futures", "futures-bounded", @@ -2460,9 +2462,11 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "quick-protobuf", + "quick-protobuf-codec", "rand 0.8.5", "rand_core 0.6.4", "scc", + "static_assertions", "thiserror", "void", ] diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 7e3ce4bc73e..f35a65bf77f 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -9,6 +9,8 @@ rust-version.workspace = true [dependencies] async-trait = "0.1" quick-protobuf = "0.8" +quick-protobuf-codec = { workspace = true } +asynchronous-codec = "0.6" libp2p-core = { workspace = true } rand_core = "0.6" rand = { version = "0.8", optional = true } @@ -21,6 +23,8 @@ either = "1.9.0" futures = "0.3.29" thiserror = "1.0.50" scc = "2.0.3" +bytes = "1" +static_assertions = "1.1.0" [lints] workspace = true diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 63eb3888cc2..72834301774 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashSet, VecDeque}, + collections::{VecDeque}, sync::Arc, task::{Context, Poll}, }; @@ -28,14 +28,14 @@ enum Command { }, } -pub struct ReachabilityTestSucc { - pub server_peer: PeerId, - pub visible_addr: Option, - pub suspicious_addrs: Vec, +pub(crate) struct ReachabilityTestSucc { + pub(crate) server_peer: PeerId, + pub(crate) visible_addr: Option, + pub(crate) suspicious_addrs: Vec, } #[derive(Debug, thiserror::Error)] -pub enum ReachabilityTestErr { +pub(crate) enum ReachabilityTestErr { #[error("Server chose not to dial any provided address. Server peer id: {peer_id}")] ServerChoseNotToDialAnyAddress { peer_id: PeerId }, #[error("Server rejected dial request. Server peer id: {peer_id}")] @@ -56,7 +56,7 @@ pub enum ReachabilityTestErr { }, } -pub enum Event { +pub(crate) enum Event { CompletedReachabilityTest(Result), } @@ -87,8 +87,8 @@ where &mut self, _connection_id: ConnectionId, peer: PeerId, - local_addr: &Multiaddr, - remote_addr: &Multiaddr, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, ) -> Result { Ok(Handler::new(self.accepted_nonce.clone(), peer)) } @@ -97,14 +97,14 @@ where &mut self, _connection_id: ConnectionId, peer: PeerId, - addr: &Multiaddr, - role_override: Endpoint, - port_use: PortUse, + _addr: &Multiaddr, + _role_override: Endpoint, + _port_use: PortUse, ) -> Result { Ok(Handler::new(self.accepted_nonce.clone(), peer)) } - fn on_swarm_event(&mut self, event: FromSwarm) { + fn on_swarm_event(&mut self, _event: FromSwarm) { todo!() } @@ -128,7 +128,7 @@ where fn poll( &mut self, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { if let Some(event) = self.pending_events.pop_front() { @@ -199,7 +199,7 @@ where server_peer, local_addrs, } => { - let cleaned_local_addrs = local_addrs.iter().filter(|addr| { + let _cleaned_local_addrs = local_addrs.iter().filter(|addr| { !addr.iter().any(|p| match p { Protocol::Ip4(ip) if !IpExt::is_global(&ip) => true, Protocol::Ip6(ip) if !IpExt::is_global(&ip) => true, diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 6db25fe01de..204a1ef6dee 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,10 +1,18 @@ +// two handlers, share state in behaviour +// do isolated stuff in async function +// +// write basic tests +// Take a look at rendezvous +// TODO: tests +// TODO: Handlers + +mod dial_back; + use std::{ cmp::min, collections::VecDeque, convert::identity, - i8::MAX, io, - iter::{once, repeat}, sync::Arc, task::Poll, time::Duration, @@ -17,13 +25,13 @@ use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use void::Void; + use crate::{ client::ToBehaviour, generated::structs::mod_DialResponse::ResponseStatus, request_response::{ - DialBack, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + DialBack, DialDataRequest, DialDataResponse, DialRequest, Request, Response, }, DIAL_BACK_PROTOCOL_NAME, REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, }; @@ -93,7 +101,7 @@ impl ConnectionHandler for Handler { type InboundProtocol = ReadyUpgrade; type OutboundProtocol = ReadyUpgrade; type InboundOpenInfo = (); - type OutboundOpenInfo = Option; + type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) @@ -209,10 +217,10 @@ impl ConnectionHandler for Handler { println!("Dropping inbound stream because we are at capacity."); } } - ConnectionEvent::DialUpgradeError(err) => { + ConnectionEvent::DialUpgradeError(_err) => { todo!() } - ConnectionEvent::ListenUpgradeError(err) => { + ConnectionEvent::ListenUpgradeError(_err) => { todo!() } ConnectionEvent::AddressChange(_) => {} diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs new file mode 100644 index 00000000000..edd490c2f8e --- /dev/null +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -0,0 +1,12 @@ +use libp2p_swarm::ConnectionHandler; + +#[derive(Debug)] +pub enum ToBehaviour { + GotNonce(u64), +} + +#[derive(Debug, Default)] +pub struct Handler; + +impl ConnectionHandler for Handler { +} diff --git a/protocols/autonatv2/src/generated/structs.proto b/protocols/autonatv2/src/generated/structs.proto index 46f5f38f1b6..7088e888052 100644 --- a/protocols/autonatv2/src/generated/structs.proto +++ b/protocols/autonatv2/src/generated/structs.proto @@ -2,56 +2,45 @@ syntax = "proto2"; package structs; - message Message { - oneof msg { - DialRequest dialRequest = 1; - DialResponse dialResponse = 2; - DialDataRequest dialDataRequest = 3; - DialDataResponse dialDataResponse = 4; - } + oneof msg { + DialRequest dialRequest = 1; + DialResponse dialResponse = 2; + DialDataRequest dialDataRequest = 3; + DialDataResponse dialDataResponse = 4; + } } - message DialRequest { - repeated bytes addrs = 1; - fixed64 nonce = 2; + repeated bytes addrs = 1; + fixed64 nonce = 2; } - message DialDataRequest { - uint32 addrIdx = 1; - uint64 numBytes = 2; + uint32 addrIdx = 1; + uint64 numBytes = 2; } - enum DialStatus { - UNUSED = 0; - E_DIAL_ERROR = 100; - E_DIAL_BACK_ERROR = 101; - OK = 200; + UNUSED = 0; + E_DIAL_ERROR = 100; + E_DIAL_BACK_ERROR = 101; + OK = 200; } - message DialResponse { - enum ResponseStatus { - E_INTERNAL_ERROR = 0; - E_REQUEST_REJECTED = 100; - E_DIAL_REFUSED = 101; - OK = 200; - } - - ResponseStatus status = 1; - uint32 addrIdx = 2; - DialStatus dialStatus = 3; + enum ResponseStatus { + E_INTERNAL_ERROR = 0; + E_REQUEST_REJECTED = 100; + E_DIAL_REFUSED = 101; + OK = 200; + } + + ResponseStatus status = 1; + uint32 addrIdx = 2; + DialStatus dialStatus = 3; } +message DialDataResponse { bytes data = 1; } -message DialDataResponse { - bytes data = 1; -} - - -message DialBack { - fixed64 nonce = 1; -} +message DialBack { fixed64 nonce = 1; } diff --git a/protocols/autonatv2/src/generated/structs.rs b/protocols/autonatv2/src/generated/structs.rs index 3fd8956178c..2bb613458e8 100644 --- a/protocols/autonatv2/src/generated/structs.rs +++ b/protocols/autonatv2/src/generated/structs.rs @@ -10,6 +10,7 @@ use std::borrow::Cow; + use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::*; @@ -54,11 +55,11 @@ impl<'a> From<&'a str> for DialStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct Message<'a> { - pub(crate) msg: structs::mod_Message::OneOfmsg<'a>, +pub(crate) struct Message { + pub(crate) msg: structs::mod_Message::OneOfmsg, } -impl<'a> MessageRead<'a> for Message<'a> { +impl<'a> MessageRead<'a> for Message { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { @@ -75,7 +76,7 @@ impl<'a> MessageRead<'a> for Message<'a> { } } -impl<'a> MessageWrite for Message<'a> { +impl MessageWrite for Message { fn get_size(&self) -> usize { 0 + match self.msg { @@ -101,15 +102,15 @@ pub(crate) mod mod_Message { use super::*; #[derive(Debug, PartialEq, Clone)] -pub(crate) enum OneOfmsg<'a> { - dialRequest(structs::DialRequest<'a>), +pub(crate) enum OneOfmsg { + dialRequest(structs::DialRequest), dialResponse(structs::DialResponse), dialDataRequest(structs::DialDataRequest), - dialDataResponse(structs::DialDataResponse<'a>), + dialDataResponse(structs::DialDataResponse), None, } -impl<'a> Default for OneOfmsg<'a> { +impl Default for OneOfmsg { fn default() -> Self { OneOfmsg::None } @@ -119,17 +120,17 @@ impl<'a> Default for OneOfmsg<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialRequest<'a> { - pub(crate) addrs: Vec>, +pub(crate) struct DialRequest { + pub(crate) addrs: Vec>, pub(crate) nonce: Option, } -impl<'a> MessageRead<'a> for DialRequest<'a> { +impl<'a> MessageRead<'a> for DialRequest { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.addrs.push(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(10) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), @@ -139,7 +140,7 @@ impl<'a> MessageRead<'a> for DialRequest<'a> { } } -impl<'a> MessageWrite for DialRequest<'a> { +impl MessageWrite for DialRequest { fn get_size(&self) -> usize { 0 + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() @@ -274,16 +275,16 @@ impl<'a> From<&'a str> for ResponseStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialDataResponse<'a> { - pub(crate) data: Option>, +pub(crate) struct DialDataResponse { + pub(crate) data: Option>, } -impl<'a> MessageRead<'a> for DialDataResponse<'a> { +impl<'a> MessageRead<'a> for DialDataResponse { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned().into()), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -292,7 +293,7 @@ impl<'a> MessageRead<'a> for DialDataResponse<'a> { } } -impl<'a> MessageWrite for DialDataResponse<'a> { +impl MessageWrite for DialDataResponse { fn get_size(&self) -> usize { 0 + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) @@ -335,4 +336,3 @@ impl MessageWrite for DialBack { Ok(()) } } - diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index db003beed6f..0547afe603e 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -1,12 +1,17 @@ -use std::{borrow::Cow, io, sync::OnceLock}; +// change to quick-protobuf-codec -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use std::{borrow::Cow, io}; + +use asynchronous_codec::{FramedRead, FramedWrite}; + +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::{ upgrade::{read_length_prefixed, write_length_prefixed}, Multiaddr, }; -use libp2p_swarm::{ConnectionId, NetworkBehaviour, StreamProtocol}; + use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; +use quick_protobuf_codec::Codec; use rand::Rng; use crate::generated::structs as proto; @@ -28,30 +33,6 @@ macro_rules! check_existence { }; } -macro_rules! read_from { - () => { - pub(crate) async fn read_from(mut reader: R) -> io::Result - where - R: AsyncRead + Unpin, - { - let bytes = read_length_prefixed(&mut reader, 1024).await?; - Self::from_bytes(&bytes) - } - }; -} - -macro_rules! write_into { - () => { - pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> - where - W: AsyncWrite + Unpin, - { - let bytes = self.into_bytes(); - write_length_prefixed(&mut writer, bytes).await - } - }; -} - #[derive(Debug, Clone)] pub(crate) enum Request { Dial(DialRequest), @@ -70,12 +51,12 @@ pub(crate) struct DialDataResponse { } impl Request { - read_from!(); - - fn from_bytes(bytes: &[u8]) -> io::Result { - let mut reader = BytesReader::from_bytes(bytes); - let msg = proto::Message::from_reader(&mut reader, bytes) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { + let mut framed_io = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)); + let msg = framed_io + .next() + .await + .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; match msg.msg { proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { let addrs: Vec = addrs @@ -100,54 +81,35 @@ impl Request { } } - write_into!(); - - fn into_bytes(self) -> Cow<'static, [u8]> { - fn make_message_bytes(request: Request) -> Vec { - let msg = match request { - Request::Dial(DialRequest { addrs, nonce }) => { - let addrs = addrs.iter().map(|e| e.to_vec().into()).collect(); - let nonce = Some(nonce); - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { - addrs, - nonce, - }), - } + pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { + let msg = match self { + Request::Dial(DialRequest { addrs, nonce }) => { + let addrs = addrs.iter().map(|e| e.to_vec()).collect(); + let nonce = Some(nonce); + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { + addrs, + nonce, + }), } - Request::Data(DialDataResponse { data_count }) => { - assert!( - data_count <= DATA_FIELD_LEN_UPPER_BOUND, - "data_count too large" - ); - static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataResponse( - proto::DialDataResponse { - data: Some(Cow::Borrowed(&DATA[..data_count])), - }, - ), - } + } + Request::Data(DialDataResponse { data_count }) => { + debug_assert!( + data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { + data: Some(Cow::Borrowed(&DATA[..data_count])), + }), } - }; - let mut buf = Vec::with_capacity(msg.get_size()); - let mut writer = Writer::new(&mut buf); - msg.write_message(&mut writer).expect("encoding to succeed"); - buf - } - // little optimization: if the data is exactly 4096 bytes, we can use a static buffer. It is - // likely that this is the case, draining the most performance. - if matches!( - self, - Self::Data(DialDataResponse { - data_count: DATA_FIELD_LEN_UPPER_BOUND - }) - ) { - static CELL: OnceLock> = OnceLock::new(); - CELL.get_or_init(move || make_message_bytes(self)).into() - } else { - make_message_bytes(self).into() - } + } + }; + FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } } @@ -171,12 +133,11 @@ pub(crate) struct DialResponse { } impl Response { - read_from!(); - - fn from_bytes(bytes: &[u8]) -> std::io::Result { - let mut reader = BytesReader::from_bytes(bytes); - let msg = proto::Message::from_reader(&mut reader, bytes) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> std::io::Result { + let msg = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)) + .next() + .await + .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; match msg.msg { proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { @@ -210,9 +171,7 @@ impl Response { } } - write_into!(); - - fn into_bytes(self) -> Vec { + pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = match self { Self::Dial(DialResponse { status, @@ -235,10 +194,10 @@ impl Response { }), }, }; - let mut buf = Vec::with_capacity(msg.get_size()); - let mut writer = Writer::new(&mut buf); - msg.write_message(&mut writer).expect("encoding to succeed"); - buf + FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } } @@ -260,33 +219,28 @@ impl DialDataRequest { const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { - pub nonce: u64, + pub(crate) nonce: u64, } impl DialBack { - read_from!(); - - fn from_bytes(bytes: &[u8]) -> io::Result { - let mut reader = BytesReader::from_bytes(bytes); - let proto::DialBack { nonce } = proto::DialBack::from_reader(&mut reader, bytes) - .map_err(|err| new_io_invalid_data_err!(err))?; + pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { + let proto::DialBack { nonce } = + FramedRead::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) + .next() + .await + .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; let nonce = check_existence!(nonce)?; Ok(Self { nonce }) } - pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> - where - W: AsyncWrite + Unpin, - { + pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = proto::DialBack { nonce: Some(self.nonce), }; - let mut buf = [0u8; DIAL_BACK_MAX_SIZE]; - debug_assert!(msg.get_size() <= DIAL_BACK_MAX_SIZE); - let mut msg_writer = Writer::new(&mut buf[..]); - msg.write_message(&mut msg_writer) - .expect("encoding to succeed"); - write_length_prefixed(&mut writer, &buf[..msg.get_size()]).await + FramedWrite::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } } From fadf904c81faeb5c749367bc28007a17fa61ac4c Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:15:44 +0100 Subject: [PATCH 05/97] WIP client --- Cargo.lock | 1 + misc/ip-global/src/lib.rs | 2 +- protocols/autonatv2/Cargo.toml | 1 + protocols/autonatv2/src/client.rs | 35 -- protocols/autonatv2/src/client/behaviour.rs | 368 +++++++++------- protocols/autonatv2/src/client/handler.rs | 396 +----------------- .../autonatv2/src/client/handler/dial_back.rs | 92 +++- .../autonatv2/src/client/handler/request.rs | 302 +++++++++++++ protocols/autonatv2/src/global_only.rs | 247 +++++++++++ protocols/autonatv2/src/lib.rs | 1 + protocols/autonatv2/src/request_response.rs | 5 +- 11 files changed, 867 insertions(+), 583 deletions(-) create mode 100644 protocols/autonatv2/src/client/handler/request.rs create mode 100644 protocols/autonatv2/src/global_only.rs diff --git a/Cargo.lock b/Cargo.lock index ef42db7cd05..09b2320c3b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2536,6 +2536,7 @@ dependencies = [ "scc", "static_assertions", "thiserror", + "tracing", "void", ] diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs index aaf7a6b2a89..e3f1fb4f2ae 100644 --- a/misc/ip-global/src/lib.rs +++ b/misc/ip-global/src/lib.rs @@ -68,7 +68,7 @@ impl Ipv4Ext for Ipv4Addr { fn is_private(&self) -> bool { match self.octets() { [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, + [172, b, ..] if (16..=31).contains(&b) => true, [192, 168, ..] => true, _ => false, } diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index f35a65bf77f..76b2cc7ed87 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -25,6 +25,7 @@ thiserror = "1.0.50" scc = "2.0.3" bytes = "1" static_assertions = "1.1.0" +tracing = "0.1.40" [lints] workspace = true diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index 350afcd31cf..016ba931f10 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,38 +1,3 @@ -use libp2p_core::Multiaddr; - -use crate::request_response::{DialRequest, DialResponse}; - mod behaviour; mod handler; -#[derive(Debug)] -enum ToBehaviour { - ResponseInfo(ResponseInfo), - OutboundError(E), -} - -#[derive(Debug)] -struct ResponseInfo { - response: DialResponse, - suspicious_addrs: Vec, - successfull_addr: Option, -} - -impl ResponseInfo { - fn new( - response: DialResponse, - suspicious_addrs: Vec, - successfull_addr: Option, - ) -> Self { - Self { - response, - suspicious_addrs, - successfull_addr, - } - } -} - -#[derive(Debug)] -enum FromBehaviour { - Dial(DialRequest), -} diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 72834301774..7eb41a2b229 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,78 +1,48 @@ use std::{ - collections::{VecDeque}, - sync::Arc, + collections::{BTreeMap, HashMap, HashSet, VecDeque}, task::{Context, Poll}, }; -use ip_global::IpExt; -use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; +use either::Either; +use libp2p_core::{multiaddr::Protocol, transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, - NetworkBehaviour, NotifyHandler, ToSwarm, + behaviour::ConnectionEstablished, + dial_opts::{DialOpts, PeerCondition}, + ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, + NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; +use rand::{seq::SliceRandom, Rng}; use rand_core::RngCore; -use crate::{ - client::{ResponseInfo, ToBehaviour}, - generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{DialRequest, DialResponse}, -}; - -use super::{handler::Handler, FromBehaviour}; - -enum Command { - TestListenerReachability { - server_peer: PeerId, - local_addrs: Vec, - }, -} - -pub(crate) struct ReachabilityTestSucc { - pub(crate) server_peer: PeerId, - pub(crate) visible_addr: Option, - pub(crate) suspicious_addrs: Vec, -} +use crate::{global_only::IpExt, request_response::DialRequest}; -#[derive(Debug, thiserror::Error)] -pub(crate) enum ReachabilityTestErr { - #[error("Server chose not to dial any provided address. Server peer id: {peer_id}")] - ServerChoseNotToDialAnyAddress { peer_id: PeerId }, - #[error("Server rejected dial request. Server peer id: {peer_id}")] - ServerRejectedDialRequest { peer_id: PeerId }, - #[error("Server ran into an internal error. Server peer id: {peer_id}")] - InternalServerError { peer_id: PeerId }, - #[error("Server did not respond correctly to dial request. Server peer id: {peer_id}")] - InvalidResponse { peer_id: PeerId }, - #[error("Server was unable to connect to address: {addr:?}. Server peer id: {peer_id}")] - UnableToConnectOnSelectedAddress { - peer_id: PeerId, - addr: Option, - }, - #[error("Server experienced failure during dial back on address: {addr:?} Server peer id: {peer_id}")] - FailureDuringDialBack { - peer_id: PeerId, - addr: Option, - }, -} +use super::handler::{ + new_handler, Handler, RequestError, RequestFromBehaviour, RequestToBehaviour, TestEnd, +}; -pub(crate) enum Event { - CompletedReachabilityTest(Result), +pub struct Config { + pub test_server_count: usize, } -pub(super) struct Behaviour +pub struct Behaviour where R: RngCore + 'static, { - pending_commands: VecDeque, + local_peers: HashSet, + pending_nonces: HashSet, + known_servers: Vec, + rng: R, + config: Config, pending_events: VecDeque< ToSwarm< ::ToSwarm, <::ConnectionHandler as ConnectionHandler>::FromBehaviour, >, >, - accepted_nonce: Arc>, - rng: R, + peers_to_handlers: HashMap, + pending_req_for_peer: HashMap>, + pending_requests: VecDeque, } impl NetworkBehaviour for Behaviour @@ -81,148 +51,240 @@ where { type ConnectionHandler = Handler; - type ToSwarm = Event; + type ToSwarm = (); fn handle_established_inbound_connection( &mut self, - _connection_id: ConnectionId, - peer: PeerId, + connection_id: ConnectionId, + _peer: PeerId, _local_addr: &Multiaddr, - _remote_addr: &Multiaddr, - ) -> Result { - Ok(Handler::new(self.accepted_nonce.clone(), peer)) + remote_addr: &Multiaddr, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + if addr_is_local(remote_addr) { + self.local_peers.insert(connection_id); + } + Ok(new_handler()) } fn handle_established_outbound_connection( &mut self, - _connection_id: ConnectionId, - peer: PeerId, - _addr: &Multiaddr, + connection_id: ConnectionId, + _peer: PeerId, + addr: &Multiaddr, _role_override: Endpoint, _port_use: PortUse, - ) -> Result { - Ok(Handler::new(self.accepted_nonce.clone(), peer)) + ) -> Result<::ConnectionHandler, ConnectionDenied> { + if addr_is_local(addr) { + self.local_peers.insert(connection_id); + } + Ok(new_handler()) } - fn on_swarm_event(&mut self, _event: FromSwarm) { - todo!() + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { + for _ in 0..self.config.test_server_count { + let nonce = self.rng.gen(); + self.pending_requests.push_back(DialRequest { + addrs: vec![addr.clone()], + nonce, + }); + self.pending_nonces.insert(nonce); + } + } + FromSwarm::ConnectionEstablished(ConnectionEstablished { + peer_id, + connection_id, + .. + }) => { + self.peers_to_handlers + .entry(peer_id) + .or_insert(connection_id); + } + FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + .. + }) => { + tracing::trace!("connection with {peer_id:?} closed"); + self.handle_no_connection(peer_id, connection_id); + } + FromSwarm::DialFailure(DialFailure { + peer_id: Some(peer_id), + error, + connection_id, + }) => { + tracing::trace!("dialing {peer_id:?} failed: {error:?}"); + self.handle_no_connection(peer_id, connection_id); + } + _ => {} + } } fn on_connection_handler_event( &mut self, peer_id: PeerId, - _connection_id: ConnectionId, - event: ::ToBehaviour, + connection_id: ConnectionId, + event: ::ToBehaviour, ) { + self.peers_to_handlers + .entry(peer_id) + .or_insert(connection_id); match event { - ToBehaviour::ResponseInfo(response_info) => { - let event = Event::CompletedReachabilityTest( - self.handle_response_info(peer_id, response_info), + Either::Right(Ok(nonce)) => { + if self.pending_nonces.remove(&nonce) { + tracing::trace!("Received pending nonce from {peer_id:?}"); + } else { + tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); + } + } + Either::Right(Err(err)) => { + tracing::debug!("Dial back failed: {:?}", err); + } + Either::Left(RequestToBehaviour::PeerHasServerSupport) => { + if !self.known_servers.contains(&peer_id) { + self.known_servers.push(peer_id); + } + } + Either::Left(RequestToBehaviour::TestCompleted(Ok(TestEnd { + dial_request: DialRequest { nonce, addrs }, + suspicious_addr, + reachable_addr, + }))) => { + if self.pending_nonces.remove(&nonce) { + tracing::debug!( + "server reported reachbility, but didn't actually reached this node." + ); + return; + } + if !suspicious_addr.is_empty() { + tracing::trace!( + "server reported suspicious addresses: {:?}", + suspicious_addr + ); + } + self.pending_events.extend( + addrs + .into_iter() + .take_while(|addr| addr != &reachable_addr) + .map(ToSwarm::ExternalAddrExpired), ); - self.pending_events.push_back(ToSwarm::GenerateEvent(event)); + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); + } + Either::Left(RequestToBehaviour::TestCompleted(Err( + RequestError::UnableToConnectOnSelectedAddress { addr: Some(addr) }, + ))) + | Either::Left(RequestToBehaviour::TestCompleted(Err( + RequestError::FailureDuringDialBack { addr: Some(addr) }, + ))) => { + self.pending_events + .push_back(ToSwarm::ExternalAddrExpired(addr)); + } + Either::Left(RequestToBehaviour::TestCompleted(Err(err))) => { + tracing::debug!("Test failed: {:?}", err); } - _ => todo!(), } - todo!() } fn poll( &mut self, _cx: &mut Context<'_>, - ) -> Poll::FromBehaviour>> - { + ) -> Poll::FromBehaviour>> { if let Some(event) = self.pending_events.pop_front() { return Poll::Ready(event); } - if let Some(command) = self.pending_commands.pop_front() { - self.handle_command(command); - return Poll::Pending; + self.pending_req_for_peer.retain(|_, reqs| !reqs.is_empty()); + for (peer, dial_requests) in &mut self.pending_req_for_peer { + if let Some(conn_id) = self.peers_to_handlers.get(peer) { + let dial_request = dial_requests.pop_front().unwrap(); + return Poll::Ready(ToSwarm::NotifyHandler { + peer_id: *peer, + handler: NotifyHandler::One(*conn_id), + event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), + }); + } } - todo!() + if let Some(dial_request) = self.pending_requests.pop_front() { + if self.known_servers.is_empty() { + self.pending_requests.push_front(dial_request); + } else { + let peer = self.known_servers.choose(&mut self.rng).unwrap(); + + self.submit_req_for_peer(*peer, dial_request); + } + } + Poll::Pending } } impl Behaviour where - R: RngCore, + R: RngCore + 'static, { - fn handle_response_info( - &mut self, - peer_id: PeerId, - ResponseInfo { - response: - DialResponse { - status, - dial_status, - .. - }, - suspicious_addrs, - successfull_addr, - }: ResponseInfo, - ) -> Result { - match (status, dial_status) { - (ResponseStatus::E_REQUEST_REJECTED, _) => { - Err(ReachabilityTestErr::ServerRejectedDialRequest { peer_id }) - } - (ResponseStatus::E_DIAL_REFUSED, _) => { - Err(ReachabilityTestErr::ServerChoseNotToDialAnyAddress { peer_id }) - } - (ResponseStatus::E_INTERNAL_ERROR, _) => { - Err(ReachabilityTestErr::InternalServerError { peer_id }) - } - (ResponseStatus::OK, DialStatus::UNUSED) => { - Err(ReachabilityTestErr::InvalidResponse { peer_id }) - } - (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { - Err(ReachabilityTestErr::UnableToConnectOnSelectedAddress { - peer_id, - addr: successfull_addr, - }) - } - (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { - Err(ReachabilityTestErr::FailureDuringDialBack { - peer_id, - addr: successfull_addr, - }) - } - (ResponseStatus::OK, DialStatus::OK) => Ok(ReachabilityTestSucc { - server_peer: peer_id, - visible_addr: successfull_addr, - suspicious_addrs, - }), + fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest) { + if let Some(conn_id) = self.peers_to_handlers.get(&peer) { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: peer, + handler: NotifyHandler::One(*conn_id), + event: Either::Left(RequestFromBehaviour::PerformRequest(req)), + }); + } else { + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(peer) + .condition(PeerCondition::DisconnectedAndNotDialing) + .build(), + }); + self.pending_req_for_peer + .entry(peer) + .or_default() + .push_back(req); } } - fn handle_command(&mut self, command: Command) { - match command { - Command::TestListenerReachability { - server_peer, - local_addrs, - } => { - let _cleaned_local_addrs = local_addrs.iter().filter(|addr| { - !addr.iter().any(|p| match p { - Protocol::Ip4(ip) if !IpExt::is_global(&ip) => true, - Protocol::Ip6(ip) if !IpExt::is_global(&ip) => true, - Protocol::Dns(m) - | Protocol::Dns4(m) - | Protocol::Dns6(m) - | Protocol::Dnsaddr(m) => m == "localhost" || m.ends_with(".local"), - _ => false, + fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { + if matches!(self.peers_to_handlers.get(&peer_id), Some(conn_id) if *conn_id == connection_id) + { + self.peers_to_handlers.remove(&peer_id); + } + for dial_request in self + .pending_req_for_peer + .remove(&peer_id) + .unwrap_or_default() + { + if let Some(new_peer) = self.known_servers.choose(&mut self.rng) { + if let Some(conn_id) = self.peers_to_handlers.get(new_peer) { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: *new_peer, + handler: NotifyHandler::One(*conn_id), + event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), }) - }); - let dial_opts = DialOpts::peer_id(server_peer.clone()).build(); - let dial_event = ToSwarm::Dial { opts: dial_opts }; - self.pending_events.push_back(dial_event); - let dial_request = DialRequest { - addrs: local_addrs, - nonce: self.rng.next_u64(), - }; - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: server_peer, - handler: NotifyHandler::Any, - event: FromBehaviour::Dial(dial_request), - }) + } else { + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(*new_peer) + .condition(PeerCondition::DisconnectedAndNotDialing) + .build(), + }); + self.pending_req_for_peer + .entry(*new_peer) + .or_default() + .push_back(dial_request); + } + } else { + self.pending_requests.push_front(dial_request); } } } } + +fn addr_is_local(addr: &Multiaddr) -> bool { + addr.iter().any(|c| match c { + Protocol::Dns(addr) + | Protocol::Dns4(addr) + | Protocol::Dns6(addr) + | Protocol::Dnsaddr(addr) => addr.ends_with(".local"), + Protocol::Ip4(ip) => !IpExt::is_global(&ip), + Protocol::Ip6(ip) => !IpExt::is_global(&ip), + _ => false, + }) +} diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 204a1ef6dee..cbd41b9da1b 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -7,397 +7,25 @@ // TODO: Handlers mod dial_back; +mod request; -use std::{ - cmp::min, - collections::VecDeque, - convert::identity, - io, - sync::Arc, - task::Poll, - time::Duration, -}; - -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; -use libp2p_core::{upgrade::ReadyUpgrade, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, -}; +use std::time::Duration; +use dial_back::Handler as DialBackHandler; +use libp2p_swarm::{ConnectionHandler, ConnectionHandlerSelect}; +use request::Handler as RequestHandler; -use crate::{ - client::ToBehaviour, - generated::structs::mod_DialResponse::ResponseStatus, - request_response::{ - DialBack, DialDataRequest, DialDataResponse, DialRequest, Request, Response, - }, - DIAL_BACK_PROTOCOL_NAME, REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, +pub use dial_back::ToBehaviour as DialBackToBehaviour; +pub use request::{ + Error as RequestError, FromBehaviour as RequestFromBehaviour, TestEnd, + ToBehaviour as RequestToBehaviour, }; -use crate::{DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND}; - -use super::ResponseInfo; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -#[derive(Debug, thiserror::Error)] -pub(super) enum Error { - #[error("io error")] - Io(#[from] io::Error), - #[error("invalid data request index: {index} (max: {max})")] - InvalidDataRequestIndex { index: usize, max: usize }, - #[error("data request too large: {len} (max: {max})")] - DataRequestTooLarge { len: usize, max: usize }, - #[error("data request too small: {len} (min: {min})")] - DataRequestTooSmall { len: usize, min: usize }, - #[error("timeout")] - Timeout(#[from] futures_bounded::Timeout), - #[error("invalid nonce: {nonce} (provided by (by))")] - WrongNonceGiven { nonce: u64, by: PeerId }, - #[error("request protocol removed by peer")] - RequestProtocolRemoved, - #[error("dial back protocol removed by me")] - DialBackProtocolRemoved, -} - -pub(super) struct Handler { - /// Queue of events to return when polled. - queued_events: VecDeque< - ConnectionHandlerEvent< - ::OutboundProtocol, - ::OutboundOpenInfo, - ::ToBehaviour, - ::Error, - >, - >, - pending_data: VecDeque, - queued_requests: VecDeque, - inbound: futures_bounded::FuturesSet>, - outbound: futures_bounded::FuturesSet>, - accepted_nonce: Arc>, - remote_peer_id: PeerId, -} - -impl Handler { - pub(super) fn new(accepted_nonce: Arc>, remote_peer_id: PeerId) -> Self { - Self { - queued_events: VecDeque::new(), - pending_data: VecDeque::new(), - queued_requests: VecDeque::new(), - inbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), - outbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), - accepted_nonce, - remote_peer_id, - } - } -} - -impl ConnectionHandler for Handler { - type Error = Error; - type ToBehaviour = super::ToBehaviour; - type FromBehaviour = super::FromBehaviour; - type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) - } - - fn poll( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> Poll< - ConnectionHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::ToBehaviour, - Self::Error, - >, - > { - if let Some(event) = self.queued_events.pop_front() { - return Poll::Ready(event); - } - - if let Some(pending_data) = self.pending_data.pop_front() { - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(REQUEST_UPGRADE, Some(pending_data)), - }); - } - - if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { - let perform_request_res = m.map_err(Error::Timeout).and_then(identity); // necessary until flatten_error stabilized - match perform_request_res { - Ok(PerformRequestStatus::Pending(pending)) => { - self.pending_data.push_back(pending); - } - Ok(PerformRequestStatus::Finished(resp_info)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::ResponseInfo(resp_info), - )) - } - Err(e) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::OutboundError(e), - )); - } - } - } - todo!() - } - - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - match event { - super::FromBehaviour::Dial(dial_req) => { - self.queued_requests.push_back(dial_req); - } - } - } - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, - info: None, - }) => { - let request = self - .queued_requests - .pop_front() - .expect("opened a stream without a penidng request"); - let request_clone = request.clone(); - if self - .outbound - .try_push(perform_dial_request(protocol, request)) - .is_err() - { - println!( - "Dropping outbound stream because we are at capacity. {request_clone:?}" - ); - self.queued_requests.push_front(request_clone); - } - } - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, - info: Some(pending_data), - }) => { - let pending_data_copy = pending_data.clone(); - if self - .outbound - .try_push(perform_data_request(protocol, pending_data)) - .is_err() - { - println!("Dropping outbound stream because we are at capacity."); - self.pending_data.push_front(pending_data_copy); - } - } - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - let remote_peer_id = self.remote_peer_id.clone(); - if self - .inbound - .try_push(perform_dial_back( - protocol, - self.accepted_nonce.clone(), - remote_peer_id, - )) - .is_err() - { - println!("Dropping inbound stream because we are at capacity."); - } - } - ConnectionEvent::DialUpgradeError(_err) => { - todo!() - } - ConnectionEvent::ListenUpgradeError(_err) => { - todo!() - } - ConnectionEvent::AddressChange(_) => {} - ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { - if protocols.any(|e| &DIAL_BACK_PROTOCOL_NAME == e) { - self.queued_events.push_back(ConnectionHandlerEvent::Close( - Error::DialBackProtocolRemoved, - )); - } - } - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { - if protocols.any(|e| &REQUEST_PROTOCOL_NAME == e) { - self.queued_events - .push_back(ConnectionHandlerEvent::Close(Error::RequestProtocolRemoved)); - } - } - _ => {} - } - todo!() - } -} - -enum PerformRequestStatus { - Finished(ResponseInfo), - Pending(PendingData), -} - -#[derive(Debug, Clone)] -pub(super) struct PendingData { - data_count: usize, - suspicious_addrs: Vec, - all_addrs: Vec, -} - -async fn perform_dial_request( - mut stream: S, - dial_req: DialRequest, -) -> Result -where - S: AsyncRead + AsyncWrite + Unpin, -{ - let addrs = dial_req.addrs.clone(); - let req = Request::Dial(dial_req); - req.write_into(&mut stream).await?; - let resp = Response::read_from(&mut stream).await?; - stream.close().await?; - let DialDataRequest { - addr_idx, - num_bytes, - } = match resp { - Response::Dial(resp) => { - let successfull_addr = if resp.status == ResponseStatus::OK { - addrs.get(resp.addr_idx).cloned() - } else { - None - }; - return Ok(PerformRequestStatus::Finished(ResponseInfo::new( - resp, - Vec::new(), - successfull_addr, - ))); - } - Response::Data(data_req) => data_req, - }; - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(Error::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); - } else if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(Error::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - let suspicious_addr = addrs - .get(addr_idx) - .ok_or(Error::InvalidDataRequestIndex { - index: addr_idx, - max: addrs.len(), - })? - .clone(); - Ok(PerformRequestStatus::Pending(PendingData { - data_count: num_bytes, - suspicious_addrs: vec![suspicious_addr], - all_addrs: addrs, - })) -} - -async fn perform_data_request( - mut stream: S, - PendingData { - mut data_count, - mut suspicious_addrs, - all_addrs, - }: PendingData, -) -> Result -where - S: AsyncRead + AsyncWrite + Unpin, -{ - let data_field_len = min(DATA_FIELD_LEN_UPPER_BOUND, data_count); - data_count -= data_field_len; - let req = Request::Data(DialDataResponse { - data_count: data_field_len, - }); - req.write_into(&mut stream).await?; - if data_count != 0 { - return Ok(PerformRequestStatus::Pending(PendingData { - data_count, - suspicious_addrs, - all_addrs, - })); - } - let resp = Response::read_from(&mut stream).await?; - stream.close().await?; - match resp { - Response::Dial(dial_resp) => { - let successfull_addr = if dial_resp.status == ResponseStatus::OK { - all_addrs.get(dial_resp.addr_idx).cloned() - } else { - None - }; - Ok(PerformRequestStatus::Finished(ResponseInfo::new( - dial_resp, - suspicious_addrs, - successfull_addr, - ))) - } - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => { - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(Error::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); - } else if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(Error::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - let suspicious_addr = - all_addrs - .get(addr_idx) - .ok_or(Error::InvalidDataRequestIndex { - index: addr_idx, - max: all_addrs.len(), - })?; - if !suspicious_addrs.contains(suspicious_addr) { - suspicious_addrs.push(suspicious_addr.clone()); - } - Ok(PerformRequestStatus::Pending(PendingData { - data_count: num_bytes, - suspicious_addrs, - all_addrs, - })) - } - } -} +pub type Handler = ConnectionHandlerSelect; -async fn perform_dial_back( - mut stream: S, - accepted_nonce: Arc>, - remote_peer_id: PeerId, -) -> Result<(), Error> -where - S: AsyncRead + AsyncWrite + Unpin, -{ - let DialBack { nonce } = DialBack::read_from(&mut stream).await?; - if !accepted_nonce.contains_async(&nonce).await { - return Err(Error::WrongNonceGiven { - nonce, - by: remote_peer_id, - }); - } - stream.close().await?; - Ok(()) +pub fn new_handler() -> Handler { + RequestHandler::new().select(DialBackHandler::new()) } diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index edd490c2f8e..7d4075b7fa9 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,12 +1,92 @@ -use libp2p_swarm::ConnectionHandler; +use std::{ + io, + task::{Context, Poll}, +}; -#[derive(Debug)] -pub enum ToBehaviour { - GotNonce(u64), +use either::Either; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use futures_bounded::{FuturesSet, Timeout}; +use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; + +use crate::request_response::DialBack; + +use super::DEFAULT_TIMEOUT; + +pub type ToBehaviour = Result>; + +pub struct Handler { + inbound: FuturesSet>, } -#[derive(Debug, Default)] -pub struct Handler; +impl Handler { + pub fn new() -> Self { + Self { + inbound: FuturesSet::new(DEFAULT_TIMEOUT, 2), + } + } +} impl ConnectionHandler for Handler { + type FromBehaviour = (); + type ToBehaviour = ToBehaviour; + type InboundProtocol = ReadyUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + result + .map_err(Either::Right) + .and_then(|e| e.map_err(Either::Left)), + )); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + if self.inbound.try_push(perform_dial_back(protocol)).is_err() { + tracing::warn!("Dial back request dropped, too many requests in flight"); + } + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { + tracing::debug!("Dial back request failed: {:?}", error); + } + _ => {} + } + } +} + +async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { + let DialBack { nonce } = DialBack::read_from(&mut stream).await?; + stream.close().await?; + Ok(nonce) } diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/request.rs new file mode 100644 index 00000000000..c08f64878c1 --- /dev/null +++ b/protocols/autonatv2/src/client/handler/request.rs @@ -0,0 +1,302 @@ +use asynchronous_codec::FramedWrite; +use futures::{ + channel::mpsc, pin_mut, select, AsyncRead, AsyncWrite, FutureExt, SinkExt, StreamExt, +}; +use futures_bounded::{FuturesSet, Timeout}; +use libp2p_core::{ + upgrade::{DeniedUpgrade, ReadyUpgrade}, + Multiaddr, +}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsAdded, ProtocolsChange, + }, + AddressChange, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; +use std::{ + collections::VecDeque, + convert::identity, + io, + iter::{once, repeat}, + pin::Pin, + task::{Context, Poll}, +}; + +use crate::{ + add, + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + request_response::{ + DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, + }, + REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, +}; + +use super::DEFAULT_TIMEOUT; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("io error")] + Io(#[from] io::Error), + #[error("invalid referenced address index: {index} (max number of addr: {max})")] + InvalidReferencedAddress { index: usize, max: usize }, + #[error("data request too large: {len} (max: {max})")] + DataRequestTooLarge { len: usize, max: usize }, + #[error("data request too small: {len} (min: {min})")] + DataRequestTooSmall { len: usize, min: usize }, + #[error("timeout")] + Timeout(#[from] futures_bounded::Timeout), + #[error("server rejected dial request")] + ServerRejectedDialRequest, + #[error("server chose not to dial any provided address")] + ServerChoseNotToDialAnyAddress, + #[error("server ran into an internal error")] + InternalServerError, + #[error("server did not respond correctly to dial request")] + InvalidResponse, + #[error("server was unable to connect to address: {addr:?}")] + UnableToConnectOnSelectedAddress { addr: Option }, + #[error("server experienced failure during dial back on address: {addr:?}")] + FailureDuringDialBack { addr: Option }, +} + + +#[derive(Debug)] +pub struct TestEnd { + pub dial_request: DialRequest, + pub suspicious_addr: Vec, + pub reachable_addr: Multiaddr, +} + +#[derive(Debug)] +pub enum ToBehaviour { + TestCompleted(Result), + PeerHasServerSupport, +} + +#[derive(Debug)] +pub enum FromBehaviour { + PerformRequest(DialRequest), +} + +pub struct Handler { + queued_events: VecDeque< + ConnectionHandlerEvent< + ::OutboundProtocol, + ::OutboundOpenInfo, + ::ToBehaviour, + >, + >, + outbound: futures_bounded::FuturesSet>, + queued_requests: VecDeque, +} + +impl Handler { + pub fn new() -> Self { + Self { + queued_events: VecDeque::new(), + outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), + queued_requests: VecDeque::new(), + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = FromBehaviour; + + type ToBehaviour = ToBehaviour; + + type InboundProtocol = DeniedUpgrade; + + type OutboundProtocol = ReadyUpgrade; + + type InboundOpenInfo = (); + + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Some(event) = self.queued_events.pop_front() { + return Poll::Ready(event); + } + if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), + )); + } + if !self.queued_requests.is_empty() { + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), + }); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + match event { + FromBehaviour::PerformRequest(req) => { + self.queued_requests.push_back(req); + } + } + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + tracing::debug!("Dial request failed: {}", error); + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => match self.queued_requests.pop_front() { + Some(dial_req) => { + if self + .outbound + .try_push(handle_substream(dial_req.clone(), protocol)) + .is_err() + { + tracing::warn!("Dial request dropped, too many requests in flight"); + self.queued_requests.push_front(dial_req); + } + } + None => { + tracing::warn!("Opened unexpected substream without a pending dial request"); + } + }, + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { + if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { + self.queued_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::PeerHasServerSupport, + )); + } + } + _ => {} + } + } +} + +async fn handle_substream( + dial_request: DialRequest, + mut substream: impl AsyncRead + AsyncWrite + Unpin, +) -> Result { + Request::Dial(dial_request.clone()) + .write_into(&mut substream) + .await?; + let mut suspicious_addr = Vec::new(); + loop { + match Response::read_from(&mut substream).await? { + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if addr_idx >= dial_request.addrs.len() { + return Err(Error::InvalidReferencedAddress { + index: addr_idx, + max: dial_request.addrs.len(), + }); + } + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(Error::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } + if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); + } + match dial_request.addrs.get(addr_idx) { + Some(addr) => { + tracing::trace!("the address {addr} is suspicious to the server, sending {num_bytes} bytes of data"); + suspicious_addr.push(addr.clone()); + } + None => { + return Err(Error::InvalidReferencedAddress { + index: addr_idx, + max: dial_request.addrs.len(), + }); + } + } + + send_aap_data(&mut substream, num_bytes).await?; + } + Response::Dial(dial_response) => { + return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); + } + } + } +} + +fn test_end_from_dial_response( + req: DialRequest, + resp: DialResponse, + suspicious_addr: Vec, +) -> Result { + if resp.addr_idx >= req.addrs.len() { + return Err(Error::InvalidReferencedAddress { + index: resp.addr_idx, + max: req.addrs.len(), + }); + } + match (resp.status, resp.dial_status) { + (ResponseStatus::E_REQUEST_REJECTED, _) => Err(Error::ServerRejectedDialRequest), + (ResponseStatus::E_DIAL_REFUSED, _) => Err(Error::ServerChoseNotToDialAnyAddress), + (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServerError), + (ResponseStatus::OK, DialStatus::UNUSED) => Err(Error::InvalidResponse), + (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { + Err(Error::UnableToConnectOnSelectedAddress { + addr: req.addrs.get(resp.addr_idx).cloned(), + }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => Err(Error::FailureDuringDialBack { + addr: req.addrs.get(resp.addr_idx).cloned(), + }), + (ResponseStatus::OK, DialStatus::OK) => req + .addrs + .get(resp.addr_idx) + .ok_or(Error::InvalidReferencedAddress { + index: resp.addr_idx, + max: req.addrs.len(), + }) + .cloned() + .map(|reachable_addr| TestEnd { + dial_request: req, + suspicious_addr, + reachable_addr, + }), + } +} + +async fn send_aap_data(mut substream: impl AsyncWrite + Unpin, num_bytes: usize) -> io::Result<()> { + let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; + let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; + for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) + .take(count_full) + .chain(once(partial_len)) + .filter(|e| *e > 0) + .map(|data_count| Request::Data(DialDataResponse { data_count })) + { + req.write_into(&mut substream).await?; + } + Ok(()) +} diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonatv2/src/global_only.rs new file mode 100644 index 00000000000..4a9b4d7846a --- /dev/null +++ b/protocols/autonatv2/src/global_only.rs @@ -0,0 +1,247 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +pub trait Ipv4Ext { + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + fn is_reserved(&self) -> bool; + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + fn is_benchmarking(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + fn is_shared(&self) -> bool; + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + fn is_private(&self) -> bool; +} + +impl Ipv4Ext for Ipv4Addr { + #[inline] + fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + #[inline] + fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + #[inline] + fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + #[inline] + fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if (16..=31).contains(&b) => true, + [192, 168, ..] => true, + _ => false, + } + } +} + +pub trait Ipv6Ext { + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + fn is_unicast_link_local(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + fn is_unique_local(&self) -> bool; + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + fn is_documentation(&self) -> bool; +} + +impl Ipv6Ext for Ipv6Addr { + #[inline] + fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + #[inline] + fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + #[inline] + fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } +} + +pub trait IpExt { + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. + #[must_use] + fn is_global(&self) -> bool; +} + +impl IpExt for Ipv4Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[inline] + fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || Ipv4Ext::is_shared(self) + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || Ipv4Ext::is_benchmarking(self) + || Ipv4Ext::is_reserved(self) + || self.is_broadcast()) + } +} + +impl IpExt for Ipv6Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[inline] + fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || Ipv6Ext::is_documentation(self) + || Ipv6Ext::is_unique_local(self) + || Ipv6Ext::is_unicast_link_local(self)) + } +} + +impl IpExt for IpAddr { + #[inline] + fn is_global(&self) -> bool { + match self { + Self::V4(v4) => IpExt::is_global(v4), + Self::V6(v6) => IpExt::is_global(v6), + } + } +} diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index ad58455ca04..ae7b1ccbccd 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -3,6 +3,7 @@ use libp2p_swarm::StreamProtocol; mod client; mod generated; +mod global_only; pub(crate) mod request_response; pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 0547afe603e..03205df440e 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -5,10 +5,7 @@ use std::{borrow::Cow, io}; use asynchronous_codec::{FramedRead, FramedWrite}; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; -use libp2p_core::{ - upgrade::{read_length_prefixed, write_length_prefixed}, - Multiaddr, -}; +use libp2p_core::Multiaddr; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use quick_protobuf_codec::Codec; From 60b4cccd9d6d4ef95583f519b86f933350f95052 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:16:20 +0100 Subject: [PATCH 06/97] Run cargo fix --- core/src/transport/and_then.rs | 2 +- misc/ip-global/src/lib.rs | 3 +- protocols/autonatv2/src/client.rs | 1 - protocols/autonatv2/src/client/behaviour.rs | 10 +++--- protocols/autonatv2/src/client/handler.rs | 7 ++-- .../autonatv2/src/client/handler/dial_back.rs | 6 ++-- .../autonatv2/src/client/handler/request.rs | 36 ++++++++----------- protocols/autonatv2/src/global_only.rs | 6 ++-- protocols/autonatv2/src/lib.rs | 4 --- protocols/autonatv2/src/request_response.rs | 1 - 10 files changed, 30 insertions(+), 46 deletions(-) diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index 128f6952a1a..fb6ed2949a4 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -20,7 +20,7 @@ use crate::{ connection::ConnectedPoint, - transport::{ListenerId, Transport, TransportError, TransportEvent, DialOpts}, + transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}, }; use either::Either; use futures::prelude::*; diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs index e3f1fb4f2ae..c469fbb7e31 100644 --- a/misc/ip-global/src/lib.rs +++ b/misc/ip-global/src/lib.rs @@ -5,8 +5,7 @@ //! //! Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) - -use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; pub trait Ipv4Ext { /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index 016ba931f10..dff2397252c 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,3 +1,2 @@ mod behaviour; mod handler; - diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 7eb41a2b229..655c6666b09 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,10 +1,10 @@ use std::{ - collections::{BTreeMap, HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, task::{Context, Poll}, }; use either::Either; -use libp2p_core::{multiaddr::Protocol, transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::ConnectionEstablished, @@ -21,11 +21,11 @@ use super::handler::{ new_handler, Handler, RequestError, RequestFromBehaviour, RequestToBehaviour, TestEnd, }; -pub struct Config { - pub test_server_count: usize, +pub(crate) struct Config { + pub(crate) test_server_count: usize, } -pub struct Behaviour +pub(crate) struct Behaviour where R: RngCore + 'static, { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index cbd41b9da1b..03b9e77eb2c 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -15,8 +15,7 @@ use dial_back::Handler as DialBackHandler; use libp2p_swarm::{ConnectionHandler, ConnectionHandlerSelect}; use request::Handler as RequestHandler; -pub use dial_back::ToBehaviour as DialBackToBehaviour; -pub use request::{ +pub(crate) use request::{ Error as RequestError, FromBehaviour as RequestFromBehaviour, TestEnd, ToBehaviour as RequestToBehaviour, }; @@ -24,8 +23,8 @@ pub use request::{ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub type Handler = ConnectionHandlerSelect; +pub(crate) type Handler = ConnectionHandlerSelect; -pub fn new_handler() -> Handler { +pub(crate) fn new_handler() -> Handler { RequestHandler::new().select(DialBackHandler::new()) } diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 7d4075b7fa9..260cf28c369 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -16,14 +16,14 @@ use crate::request_response::DialBack; use super::DEFAULT_TIMEOUT; -pub type ToBehaviour = Result>; +pub(crate) type ToBehaviour = Result>; -pub struct Handler { +pub(crate) struct Handler { inbound: FuturesSet>, } impl Handler { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { inbound: FuturesSet::new(DEFAULT_TIMEOUT, 2), } diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/request.rs index c08f64878c1..21b8879b19a 100644 --- a/protocols/autonatv2/src/client/handler/request.rs +++ b/protocols/autonatv2/src/client/handler/request.rs @@ -1,18 +1,13 @@ -use asynchronous_codec::FramedWrite; -use futures::{ - channel::mpsc, pin_mut, select, AsyncRead, AsyncWrite, FutureExt, SinkExt, StreamExt, -}; -use futures_bounded::{FuturesSet, Timeout}; +use futures::{AsyncRead, AsyncWrite}; +use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, }; -use libp2p_identity::PeerId; + use libp2p_swarm::{ - handler::{ - ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsAdded, ProtocolsChange, - }, - AddressChange, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; use std::{ @@ -20,12 +15,10 @@ use std::{ convert::identity, io, iter::{once, repeat}, - pin::Pin, task::{Context, Poll}, }; use crate::{ - add, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, @@ -37,7 +30,7 @@ use crate::{ use super::DEFAULT_TIMEOUT; #[derive(Debug, thiserror::Error)] -pub enum Error { +pub(crate) enum Error { #[error("io error")] Io(#[from] io::Error), #[error("invalid referenced address index: {index} (max number of addr: {max})")] @@ -62,26 +55,25 @@ pub enum Error { FailureDuringDialBack { addr: Option }, } - #[derive(Debug)] -pub struct TestEnd { - pub dial_request: DialRequest, - pub suspicious_addr: Vec, - pub reachable_addr: Multiaddr, +pub(crate) struct TestEnd { + pub(crate) dial_request: DialRequest, + pub(crate) suspicious_addr: Vec, + pub(crate) reachable_addr: Multiaddr, } #[derive(Debug)] -pub enum ToBehaviour { +pub(crate) enum ToBehaviour { TestCompleted(Result), PeerHasServerSupport, } #[derive(Debug)] -pub enum FromBehaviour { +pub(crate) enum FromBehaviour { PerformRequest(DialRequest), } -pub struct Handler { +pub(crate) struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< ::OutboundProtocol, @@ -94,7 +86,7 @@ pub struct Handler { } impl Handler { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonatv2/src/global_only.rs index 4a9b4d7846a..e549030eff9 100644 --- a/protocols/autonatv2/src/global_only.rs +++ b/protocols/autonatv2/src/global_only.rs @@ -1,6 +1,6 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -pub trait Ipv4Ext { +pub(crate) trait Ipv4Ext { /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since @@ -67,7 +67,7 @@ impl Ipv4Ext for Ipv4Addr { } } -pub trait Ipv6Ext { +pub(crate) trait Ipv6Ext { /// Returns `true` if the address is a unicast address with link-local scope, /// as defined in [RFC 4291]. /// @@ -129,7 +129,7 @@ impl Ipv6Ext for Ipv6Addr { } } -pub trait IpExt { +pub(crate) trait IpExt { /// Returns [`true`] if the address appears to be globally routable. /// /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index ae7b1ccbccd..db7605757ee 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -15,10 +15,6 @@ pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); -pub(crate) use request_response::DATA_FIELD_LEN_UPPER_BOUND; -pub(crate) use request_response::DATA_LEN_LOWER_BOUND; -pub(crate) use request_response::DATA_LEN_UPPER_BOUND; - pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 03205df440e..5551d9ab4a9 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -7,7 +7,6 @@ use asynchronous_codec::{FramedRead, FramedWrite}; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::Multiaddr; -use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use quick_protobuf_codec::Codec; use rand::Rng; From 71487fcf5bdc6b9b2ef59d69ff5889a3023cec05 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:04:13 +0100 Subject: [PATCH 07/97] Correct minor things --- protocols/autonatv2/src/client/behaviour.rs | 36 +++++++++---------- .../autonatv2/src/client/handler/request.rs | 4 +-- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 655c6666b09..d6835507fd9 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -191,8 +191,9 @@ where &mut self, _cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); + let pending_event = self.poll_pending_events(); + if pending_event.is_ready() { + return pending_event; } self.pending_req_for_peer.retain(|_, reqs| !reqs.is_empty()); for (peer, dial_requests) in &mut self.pending_req_for_peer { @@ -210,8 +211,8 @@ where self.pending_requests.push_front(dial_request); } else { let peer = self.known_servers.choose(&mut self.rng).unwrap(); - self.submit_req_for_peer(*peer, dial_request); + return self.poll_pending_events(); } } Poll::Pending @@ -253,28 +254,23 @@ where .unwrap_or_default() { if let Some(new_peer) = self.known_servers.choose(&mut self.rng) { - if let Some(conn_id) = self.peers_to_handlers.get(new_peer) { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: *new_peer, - handler: NotifyHandler::One(*conn_id), - event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), - }) - } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(*new_peer) - .condition(PeerCondition::DisconnectedAndNotDialing) - .build(), - }); - self.pending_req_for_peer - .entry(*new_peer) - .or_default() - .push_back(dial_request); - } + self.submit_req_for_peer(*new_peer, dial_request); } else { self.pending_requests.push_front(dial_request); } } } + + fn poll_pending_events( + &mut self, + ) -> Poll< + ToSwarm<::ToSwarm, ::FromBehaviour>, + > { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + Poll::Pending + } } fn addr_is_local(addr: &Multiaddr) -> bool { diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/request.rs index 21b8879b19a..6a543c31ab6 100644 --- a/protocols/autonatv2/src/client/handler/request.rs +++ b/protocols/autonatv2/src/client/handler/request.rs @@ -46,7 +46,7 @@ pub(crate) enum Error { #[error("server chose not to dial any provided address")] ServerChoseNotToDialAnyAddress, #[error("server ran into an internal error")] - InternalServerError, + InternalServer, #[error("server did not respond correctly to dial request")] InvalidResponse, #[error("server was unable to connect to address: {addr:?}")] @@ -253,7 +253,7 @@ fn test_end_from_dial_response( match (resp.status, resp.dial_status) { (ResponseStatus::E_REQUEST_REJECTED, _) => Err(Error::ServerRejectedDialRequest), (ResponseStatus::E_DIAL_REFUSED, _) => Err(Error::ServerChoseNotToDialAnyAddress), - (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServerError), + (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServer), (ResponseStatus::OK, DialStatus::UNUSED) => Err(Error::InvalidResponse), (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { Err(Error::UnableToConnectOnSelectedAddress { From 63b31e966432ebf440fe0d5273b6ea437ec75330 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:17:57 +0100 Subject: [PATCH 08/97] Implement suggestions --- protocols/autonatv2/src/client/behaviour.rs | 136 +++++++++--------- protocols/autonatv2/src/client/handler.rs | 20 +-- .../autonatv2/src/client/handler/dial_back.rs | 7 +- .../handler/{request.rs => dial_request.rs} | 96 +++++++++---- protocols/autonatv2/src/request_response.rs | 34 ++--- 5 files changed, 170 insertions(+), 123 deletions(-) rename protocols/autonatv2/src/client/handler/{request.rs => dial_request.rs} (77%) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index d6835507fd9..d82e4eac17b 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,28 +1,44 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, task::{Context, Poll}, + time::{Duration, Instant}, }; use either::Either; use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - behaviour::ConnectionEstablished, + behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, dial_opts::{DialOpts, PeerCondition}, ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; -use rand::{seq::SliceRandom, Rng}; +use rand::{distributions::Standard, seq::SliceRandom, Rng}; use rand_core::RngCore; use crate::{global_only::IpExt, request_response::DialRequest}; -use super::handler::{ - new_handler, Handler, RequestError, RequestFromBehaviour, RequestToBehaviour, TestEnd, -}; +use super::handler::{dial_back, dial_request, Handler, TestEnd}; + +struct IntervalTicker { + interval: Duration, + last_tick: Instant, +} + +impl IntervalTicker { + fn ready(&mut self) -> bool { + if self.last_tick.elapsed() >= self.interval { + self.last_tick = Instant::now(); + true + } else { + false + } + } +} pub(crate) struct Config { pub(crate) test_server_count: usize, + pub(crate) max_addrs_count: usize, } pub(crate) struct Behaviour @@ -40,9 +56,9 @@ where <::ConnectionHandler as ConnectionHandler>::FromBehaviour, >, >, + address_candidates: HashMap, peers_to_handlers: HashMap, - pending_req_for_peer: HashMap>, - pending_requests: VecDeque, + ticker: IntervalTicker, } impl NetworkBehaviour for Behaviour @@ -63,7 +79,7 @@ where if addr_is_local(remote_addr) { self.local_peers.insert(connection_id); } - Ok(new_handler()) + Ok(Either::Left(dial_request::Handler::new())) } fn handle_established_outbound_connection( @@ -77,20 +93,16 @@ where if addr_is_local(addr) { self.local_peers.insert(connection_id); } - Ok(new_handler()) + Ok(Either::Right(dial_back::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - for _ in 0..self.config.test_server_count { - let nonce = self.rng.gen(); - self.pending_requests.push_back(DialRequest { - addrs: vec![addr.clone()], - nonce, - }); - self.pending_nonces.insert(nonce); - } + *self.address_candidates.entry(addr.clone()).or_default() += 1; + } + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { + self.address_candidates.remove(addr); } FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, @@ -141,12 +153,12 @@ where Either::Right(Err(err)) => { tracing::debug!("Dial back failed: {:?}", err); } - Either::Left(RequestToBehaviour::PeerHasServerSupport) => { + Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { if !self.known_servers.contains(&peer_id) { self.known_servers.push(peer_id); } } - Either::Left(RequestToBehaviour::TestCompleted(Ok(TestEnd { + Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { dial_request: DialRequest { nonce, addrs }, suspicious_addr, reachable_addr, @@ -172,16 +184,16 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } - Either::Left(RequestToBehaviour::TestCompleted(Err( - RequestError::UnableToConnectOnSelectedAddress { addr: Some(addr) }, - ))) - | Either::Left(RequestToBehaviour::TestCompleted(Err( - RequestError::FailureDuringDialBack { addr: Some(addr) }, - ))) => { + Either::Left(dial_request::ToBehaviour::TestCompleted( + Err(dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }) + )) + | Either::Left(dial_request::ToBehaviour::TestCompleted( + Err(dial_request::Error::FailureDuringDialBack { addr: Some(addr) }) + )) => { self.pending_events .push_back(ToSwarm::ExternalAddrExpired(addr)); } - Either::Left(RequestToBehaviour::TestCompleted(Err(err))) => { + Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { tracing::debug!("Test failed: {:?}", err); } } @@ -195,24 +207,35 @@ where if pending_event.is_ready() { return pending_event; } - self.pending_req_for_peer.retain(|_, reqs| !reqs.is_empty()); - for (peer, dial_requests) in &mut self.pending_req_for_peer { - if let Some(conn_id) = self.peers_to_handlers.get(peer) { - let dial_request = dial_requests.pop_front().unwrap(); - return Poll::Ready(ToSwarm::NotifyHandler { - peer_id: *peer, - handler: NotifyHandler::One(*conn_id), - event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), - }); - } - } - if let Some(dial_request) = self.pending_requests.pop_front() { - if self.known_servers.is_empty() { - self.pending_requests.push_front(dial_request); + if self.ticker.ready() && !self.known_servers.is_empty() { + let mut entries = self.address_candidates.drain().collect::>(); + entries.sort_unstable_by_key(|(_, count)| *count); + let addrs = entries + .into_iter() + .rev() + .map(|(addr, _)| addr) + .take(self.config.max_addrs_count) + .collect::>(); + let peers = if self.known_servers.len() < self.config.test_server_count { + self.known_servers.clone() } else { - let peer = self.known_servers.choose(&mut self.rng).unwrap(); - self.submit_req_for_peer(*peer, dial_request); - return self.poll_pending_events(); + self.known_servers + .choose_multiple(&mut self.rng, self.config.test_server_count) + .copied() + .collect() + }; + for peer in peers { + let nonce = self.rng.gen(); + let req = DialRequest { + nonce, + addrs: addrs.clone(), + }; + self.pending_nonces.insert(nonce); + self.submit_req_for_peer(peer, req); + } + let pending_event = self.poll_pending_events(); + if pending_event.is_ready() { + return pending_event; } } Poll::Pending @@ -228,18 +251,13 @@ where self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, handler: NotifyHandler::One(*conn_id), - event: Either::Left(RequestFromBehaviour::PerformRequest(req)), + event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), }); } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer) - .condition(PeerCondition::DisconnectedAndNotDialing) - .build(), - }); - self.pending_req_for_peer - .entry(peer) - .or_default() - .push_back(req); + tracing::debug!( + "There should be a connection to {:?}, but there isn't", + peer + ); } } @@ -248,17 +266,7 @@ where { self.peers_to_handlers.remove(&peer_id); } - for dial_request in self - .pending_req_for_peer - .remove(&peer_id) - .unwrap_or_default() - { - if let Some(new_peer) = self.known_servers.choose(&mut self.rng) { - self.submit_req_for_peer(*new_peer, dial_request); - } else { - self.pending_requests.push_front(dial_request); - } - } + self.known_servers.retain(|p| p != &peer_id); } fn poll_pending_events( diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 03b9e77eb2c..9e8389bb630 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -6,25 +6,15 @@ // TODO: tests // TODO: Handlers -mod dial_back; -mod request; +pub(super) mod dial_back; +pub(super) mod dial_request; +use either::Either; use std::time::Duration; -use dial_back::Handler as DialBackHandler; -use libp2p_swarm::{ConnectionHandler, ConnectionHandlerSelect}; -use request::Handler as RequestHandler; - -pub(crate) use request::{ - Error as RequestError, FromBehaviour as RequestFromBehaviour, TestEnd, - ToBehaviour as RequestToBehaviour, -}; +pub(crate) use dial_request::TestEnd; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub(crate) type Handler = ConnectionHandlerSelect; - -pub(crate) fn new_handler() -> Handler { - RequestHandler::new().select(DialBackHandler::new()) -} +pub(crate) type Handler = Either; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 260cf28c369..f1a8a618916 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,4 +1,5 @@ use std::{ + convert::identity, io, task::{Context, Poll}, }; @@ -16,7 +17,7 @@ use crate::request_response::DialBack; use super::DEFAULT_TIMEOUT; -pub(crate) type ToBehaviour = Result>; +pub(crate) type ToBehaviour = Result; pub(crate) struct Handler { inbound: FuturesSet>, @@ -51,8 +52,8 @@ impl ConnectionHandler for Handler { if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( result - .map_err(Either::Right) - .and_then(|e| e.map_err(Either::Left)), + .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) + .and_then(identity), )); } Poll::Pending diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs similarity index 77% rename from protocols/autonatv2/src/client/handler/request.rs rename to protocols/autonatv2/src/client/handler/dial_request.rs index 6a543c31ab6..a26fe4e458d 100644 --- a/protocols/autonatv2/src/client/handler/request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,4 +1,4 @@ -use futures::{AsyncRead, AsyncWrite}; +use futures::{channel::oneshot, AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -6,8 +6,12 @@ use libp2p_core::{ }; use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, + handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, + ProtocolsChange, + }, + ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, + SubstreamProtocol, }; use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; use std::{ @@ -53,6 +57,10 @@ pub(crate) enum Error { UnableToConnectOnSelectedAddress { addr: Option }, #[error("server experienced failure during dial back on address: {addr:?}")] FailureDuringDialBack { addr: Option }, + #[error("error during substream upgrad")] + SubstreamError( + #[from] StreamUpgradeError< as OutboundUpgradeSend>::Error>, + ), } #[derive(Debug)] @@ -82,7 +90,14 @@ pub(crate) struct Handler { >, >, outbound: futures_bounded::FuturesSet>, - queued_requests: VecDeque, + queued_streams: VecDeque< + oneshot::Sender< + Result< + Stream, + StreamUpgradeError< as OutboundUpgradeSend>::Error>, + >, + >, + >, } impl Handler { @@ -90,7 +105,23 @@ impl Handler { Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), - queued_requests: VecDeque::new(), + queued_streams: VecDeque::default(), + } + } + + fn perform_request(&mut self, req: DialRequest) { + let (tx, rx) = oneshot::channel(); + self.queued_streams.push_back(tx); + self.queued_events + .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), + }); + if self + .outbound + .try_push(start_substream_handle(req, rx)) + .is_err() + { + tracing::debug!("Dial request dropped, too many requests in flight"); } } } @@ -126,18 +157,13 @@ impl ConnectionHandler for Handler { ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), )); } - if !self.queued_requests.is_empty() { - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), - }); - } Poll::Pending } fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { match event { FromBehaviour::PerformRequest(req) => { - self.queued_requests.push_back(req); + self.perform_request(req); } } } @@ -154,18 +180,25 @@ impl ConnectionHandler for Handler { match event { ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { tracing::debug!("Dial request failed: {}", error); + match self.queued_streams.pop_front() { + Some(stream_tx) => { + if let Err(_) = stream_tx.send(Err(error)) { + tracing::warn!("Failed to send stream to dead handler"); + } + } + None => { + tracing::warn!( + "Opened unexpected substream without a pending dial request" + ); + } + } } ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { protocol, .. - }) => match self.queued_requests.pop_front() { - Some(dial_req) => { - if self - .outbound - .try_push(handle_substream(dial_req.clone(), protocol)) - .is_err() - { - tracing::warn!("Dial request dropped, too many requests in flight"); - self.queued_requests.push_front(dial_req); + }) => match self.queued_streams.pop_front() { + Some(stream_tx) => { + if let Err(_) = stream_tx.send(Ok(protocol)) { + tracing::warn!("Failed to send stream to dead handler"); } } None => { @@ -174,10 +207,9 @@ impl ConnectionHandler for Handler { }, ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { - self.queued_events - .push_back(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::PeerHasServerSupport, - )); + self.queued_events.push_back( + ConnectionHandlerEvent::NotifyBehaviour(ToBehaviour::PeerHasServerSupport) + ); } } _ => {} @@ -185,6 +217,22 @@ impl ConnectionHandler for Handler { } } +async fn start_substream_handle( + dial_request: DialRequest, + substream_recv: oneshot::Receiver< + Result< + Stream, + StreamUpgradeError< as OutboundUpgradeSend>::Error>, + >, + >, +) -> Result { + match substream_recv.await { + Ok(Ok(substream)) => handle_substream(dial_request, substream).await, + Ok(Err(err)) => Err(Error::from(err)), + Err(_) => Err(Error::InternalServer), + } +} + async fn handle_substream( dial_request: DialRequest, mut substream: impl AsyncRead + AsyncWrite + Unpin, diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 5551d9ab4a9..63bf8451350 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -59,9 +59,9 @@ impl Request { .into_iter() .map(|e| e.to_vec()) .map(|e| { - Multiaddr::try_from(e).map_err(|err| { - new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) - }) + Multiaddr::try_from(e).map_err( + |err| new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + ) }) .collect::, io::Error>>()?; let nonce = check_existence!(nonce)?; @@ -83,10 +83,9 @@ impl Request { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); let nonce = Some(nonce); proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { - addrs, - nonce, - }), + msg: proto::mod_Message::OneOfmsg::dialRequest( + proto::DialRequest { addrs, nonce } + ), } } Request::Data(DialDataResponse { data_count }) => { @@ -150,10 +149,9 @@ impl Response { dial_status, })) } - proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx, - numBytes, - }) => { + proto::mod_Message::OneOfmsg::dialDataRequest( + proto::DialDataRequest { addrIdx, numBytes } + ) => { let addr_idx = check_existence!(addrIdx)? as usize; let num_bytes = check_existence!(numBytes)? as usize; Ok(Self::Data(DialDataRequest { @@ -183,12 +181,14 @@ impl Response { Self::Data(DialDataRequest { addr_idx, num_bytes, - }) => proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), - }), - }, + }) => { + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + } + } }; FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) .send(msg) From c16235fe79fe32f0cd09811c4bcb54ba04bd5246 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:20:00 +0100 Subject: [PATCH 09/97] Remove gloabl only --- Cargo.lock | 8 +- Cargo.toml | 1 - core/Cargo.toml | 1 - core/src/transport/global_only.rs | 252 ++++++++++++++++++++++++++++- misc/ip-global/Cargo.toml | 12 -- misc/ip-global/src/lib.rs | 254 ------------------------------ protocols/autonatv2/Cargo.toml | 3 +- 7 files changed, 253 insertions(+), 278 deletions(-) delete mode 100644 misc/ip-global/Cargo.toml delete mode 100644 misc/ip-global/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 007e3147b05..66efb5211f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2297,10 +2297,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "ip-global" -version = "0.1.0" - [[package]] name = "ipconfig" version = "0.3.2" @@ -2518,12 +2514,11 @@ name = "libp2p-autonatv2" version = "0.1.0" dependencies = [ "async-trait", - "asynchronous-codec 0.6.2", + "asynchronous-codec", "bytes", "either", "futures", "futures-bounded", - "ip-global", "libp2p-core", "libp2p-identity", "libp2p-swarm", @@ -2565,7 +2560,6 @@ dependencies = [ "futures", "futures-timer", "instant", - "ip-global", "libp2p-identity", "libp2p-mplex", "libp2p-noise", diff --git a/Cargo.toml b/Cargo.toml index f419b6351a6..900e0e2bc46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ members = [ "misc/allow-block-list", "misc/connection-limits", "misc/futures-bounded", - "misc/ip-global", "misc/keygen", "misc/memory-connection-limits", "misc/metrics", diff --git a/core/Cargo.toml b/core/Cargo.toml index 722ea9fb611..7380695023b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,7 +16,6 @@ fnv = "1.0" futures = { version = "0.3.29", features = ["executor", "thread-pool"] } futures-timer = "3" instant = "0.1.12" -ip-global = { workspace = true } libp2p-identity = { workspace = true, features = ["peerid", "ed25519"] } multiaddr = { workspace = true } multihash = { workspace = true } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 0a1779b2375..2963cc955d0 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -17,12 +17,262 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +// +mod ip { + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + + trait Ipv4Ext { + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + fn is_reserved(&self) -> bool; + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + fn is_benchmarking(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + fn is_shared(&self) -> bool; + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + fn is_private(&self) -> bool; + } + + impl Ipv4Ext for Ipv4Addr { + #[inline] + fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + #[inline] + fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + #[inline] + fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + #[inline] + fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if (16..=31).contains(&b) => true, + [192, 168, ..] => true, + _ => false, + } + } + } + + trait Ipv6Ext { + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + fn is_unicast_link_local(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + fn is_unique_local(&self) -> bool; + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + fn is_documentation(&self) -> bool; + } + + impl Ipv6Ext for Ipv6Addr { + #[inline] + fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + #[inline] + fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + #[inline] + fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } + } + + pub(super) trait IpExt { + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. + #[must_use] + fn is_global(&self) -> bool; + } + + impl IpExt for Ipv4Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[inline] + fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || Ipv4Ext::is_shared(self) + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || Ipv4Ext::is_benchmarking(self) + || Ipv4Ext::is_reserved(self) + || self.is_broadcast()) + } + } + + impl IpExt for Ipv6Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[inline] + fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || Ipv6Ext::is_documentation(self) + || Ipv6Ext::is_unique_local(self) + || Ipv6Ext::is_unicast_link_local(self)) + } + } + + impl IpExt for IpAddr { + #[inline] + fn is_global(&self) -> bool { + match self { + Self::V4(v4) => IpExt::is_global(v4), + Self::V6(v6) => IpExt::is_global(v6), + } + } + } +} use crate::{ multiaddr::{Multiaddr, Protocol}, transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; -use ip_global::*; +use ip::IpExt; use std::{ pin::Pin, task::{Context, Poll}, diff --git a/misc/ip-global/Cargo.toml b/misc/ip-global/Cargo.toml deleted file mode 100644 index 7dfb5c3dd6d..00000000000 --- a/misc/ip-global/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "ip-global" -version = "0.1.0" -edition = "2021" -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[lints] -workspace = true diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs deleted file mode 100644 index c469fbb7e31..00000000000 --- a/misc/ip-global/src/lib.rs +++ /dev/null @@ -1,254 +0,0 @@ -//! This crate provides extension traits for [`Ipv4Addr`], [`Ipv6Addr`] and [`IpAddr`] that provide methods -//! to check if an address is reserved for special use. -//! -//! This is primarily a polyfill for `ip` feature, which is currently unstable. -//! -//! Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) - -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -pub trait Ipv4Ext { - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - fn is_reserved(&self) -> bool; - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - fn is_benchmarking(&self) -> bool; - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - fn is_shared(&self) -> bool; - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - fn is_private(&self) -> bool; -} - -impl Ipv4Ext for Ipv4Addr { - #[inline] - fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - #[inline] - fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - #[inline] - fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - #[inline] - fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if (16..=31).contains(&b) => true, - [192, 168, ..] => true, - _ => false, - } - } -} - -pub trait Ipv6Ext { - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - fn is_unicast_link_local(&self) -> bool; - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - fn is_unique_local(&self) -> bool; - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - fn is_documentation(&self) -> bool; -} - -impl Ipv6Ext for Ipv6Addr { - #[inline] - fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - #[inline] - fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - #[inline] - fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } -} - -pub trait IpExt { - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. - #[must_use] - fn is_global(&self) -> bool; -} - -impl IpExt for Ipv4Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[inline] - fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || Ipv4Ext::is_shared(self) - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || Ipv4Ext::is_benchmarking(self) - || Ipv4Ext::is_reserved(self) - || self.is_broadcast()) - } -} - -impl IpExt for Ipv6Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[inline] - fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || Ipv6Ext::is_documentation(self) - || Ipv6Ext::is_unique_local(self) - || Ipv6Ext::is_unicast_link_local(self)) - } -} - -impl IpExt for IpAddr { - #[inline] - fn is_global(&self) -> bool { - match self { - Self::V4(v4) => IpExt::is_global(v4), - Self::V6(v6) => IpExt::is_global(v6), - } - } -} diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 76b2cc7ed87..8729108ab08 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -10,14 +10,13 @@ rust-version.workspace = true async-trait = "0.1" quick-protobuf = "0.8" quick-protobuf-codec = { workspace = true } -asynchronous-codec = "0.6" +asynchronous-codec = "0.7" libp2p-core = { workspace = true } rand_core = "0.6" rand = { version = "0.8", optional = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } futures-bounded = { workspace = true } -ip-global = { workspace = true } void = "1.0.2" either = "1.9.0" futures = "0.3.29" From 3bc107e38f7123a40b3625a687bd6d96b5147252 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:31:24 +0100 Subject: [PATCH 10/97] Initial server implementation --- .../autonatv2/src/client/handler/dial_back.rs | 6 +- protocols/autonatv2/src/lib.rs | 3 + protocols/autonatv2/src/request_response.rs | 38 +-- protocols/autonatv2/src/server.rs | 2 + protocols/autonatv2/src/server/behaviour.rs | 179 ++++++++++++ protocols/autonatv2/src/server/handler.rs | 6 + .../autonatv2/src/server/handler/dial_back.rs | 139 +++++++++ .../src/server/handler/dial_request.rs | 264 ++++++++++++++++++ 8 files changed, 615 insertions(+), 22 deletions(-) create mode 100644 protocols/autonatv2/src/server.rs create mode 100644 protocols/autonatv2/src/server/behaviour.rs create mode 100644 protocols/autonatv2/src/server/handler.rs create mode 100644 protocols/autonatv2/src/server/handler/dial_back.rs create mode 100644 protocols/autonatv2/src/server/handler/dial_request.rs diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index f1a8a618916..f4505335f2c 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -13,14 +13,14 @@ use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use crate::request_response::DialBack; +use crate::{request_response::DialBack, Nonce}; use super::DEFAULT_TIMEOUT; -pub(crate) type ToBehaviour = Result; +pub(crate) type ToBehaviour = io::Result; pub(crate) struct Handler { - inbound: FuturesSet>, + inbound: FuturesSet>, } impl Handler { diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index db7605757ee..5e357b5cf0e 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -2,6 +2,7 @@ use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; mod client; +mod server; mod generated; mod global_only; pub(crate) mod request_response; @@ -15,6 +16,8 @@ pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); +pub type Nonce = u64; + pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 63bf8451350..b87852c7302 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -10,7 +10,7 @@ use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; use rand::Rng; -use crate::generated::structs as proto; +use crate::{generated::structs as proto, Nonce}; const REQUEST_MAX_SIZE: usize = 4104; pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; @@ -59,9 +59,9 @@ impl Request { .into_iter() .map(|e| e.to_vec()) .map(|e| { - Multiaddr::try_from(e).map_err( - |err| new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) - ) + Multiaddr::try_from(e).map_err(|err| { + new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + }) }) .collect::, io::Error>>()?; let nonce = check_existence!(nonce)?; @@ -83,9 +83,10 @@ impl Request { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); let nonce = Some(nonce); proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest( - proto::DialRequest { addrs, nonce } - ), + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { + addrs, + nonce, + }), } } Request::Data(DialDataResponse { data_count }) => { @@ -149,9 +150,10 @@ impl Response { dial_status, })) } - proto::mod_Message::OneOfmsg::dialDataRequest( - proto::DialDataRequest { addrIdx, numBytes } - ) => { + proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx, + numBytes, + }) => { let addr_idx = check_existence!(addrIdx)? as usize; let num_bytes = check_existence!(numBytes)? as usize; Ok(Self::Data(DialDataRequest { @@ -181,14 +183,12 @@ impl Response { Self::Data(DialDataRequest { addr_idx, num_bytes, - }) => { - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), - }), - } - } + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + }, }; FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) .send(msg) @@ -215,7 +215,7 @@ impl DialDataRequest { const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { - pub(crate) nonce: u64, + pub(crate) nonce: Nonce, } impl DialBack { diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs new file mode 100644 index 00000000000..dff2397252c --- /dev/null +++ b/protocols/autonatv2/src/server.rs @@ -0,0 +1,2 @@ +mod behaviour; +mod handler; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs new file mode 100644 index 00000000000..a96d412a0b7 --- /dev/null +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -0,0 +1,179 @@ +use std::{ + collections::{HashMap, VecDeque}, + task::{Context, Poll}, +}; + +use either::Either; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + dial_opts::{DialOpts, PeerCondition}, + ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NotifyHandler, + ToSwarm, +}; +use rand_core::RngCore; + +use super::handler::{ + dial_back, + dial_request::{self, DialBackCommand}, + Handler, +}; + +pub struct Behaviour +where + R: Clone + Send + RngCore + 'static, +{ + handlers: HashMap<(Multiaddr, PeerId), ConnectionId>, + pending_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, + dialing_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, + pending_events: VecDeque< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + >, + rng: R, +} + +impl Behaviour +where + R: RngCore + Send + Clone + 'static, +{ + pub(crate) fn new(rng: R) -> Self { + Self { + handlers: HashMap::new(), + pending_dial_back: HashMap::new(), + dialing_dial_back: HashMap::new(), + pending_events: VecDeque::new(), + rng, + } + } + + fn poll_pending_events( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + > { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + Poll::Pending + } +} + +impl NetworkBehaviour for Behaviour +where + R: RngCore + Send + Clone + 'static, +{ + type ConnectionHandler = Handler; + + type ToSwarm = (); + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _peer: PeerId, + _local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + Ok(Either::Right(dial_request::Handler::new( + remote_addr.clone(), + self.rng.clone(), + ))) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + if port_use == PortUse::New { + self.handlers.insert((addr.clone(), peer), connection_id); + } + Ok(Either::Left(dial_back::Handler::new())) + } + + fn on_swarm_event(&mut self, event: FromSwarm) {} + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + _connection_id: ConnectionId, + event: as ConnectionHandler>::ToBehaviour, + ) { + if let Either::Right(m) = event { + match m { + Ok(cmd) => { + let addr = cmd.addr.clone(); + if let Some(connection_id) = self.handlers.get(&(addr.clone(), peer_id)) { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(*connection_id), + event: Either::Left(cmd), + }); + } else { + if let Some(pending) = + self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) + { + pending.push_back(cmd); + } else { + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(peer_id.clone()) + .condition(PeerCondition::Always) + .addresses(vec![addr.clone()]) + .allocate_new_port() + .build(), + }); + self.dialing_dial_back + .insert((addr, peer_id), VecDeque::from([cmd])); + } + } + } + Err(e) => { + tracing::warn!("incoming dial request failed: {}", e); + } + } + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll as ConnectionHandler>::FromBehaviour>> { + let pending_event = self.poll_pending_events(cx); + if pending_event.is_ready() { + return pending_event; + } + if let Some((addr, peer)) = self + .dialing_dial_back + .keys() + .filter(|k| self.handlers.contains_key(*k)) + .next() + .cloned() + { + let cmds = self + .dialing_dial_back + .remove(&(addr.clone(), peer)) + .unwrap(); + let cmd_n = cmds.len(); + for cmd in cmds { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: peer.clone(), + handler: NotifyHandler::One(self.handlers[&(addr.clone(), peer)]), + event: Either::Left(cmd), + }); + } + if cmd_n > 0 { + return self.poll_pending_events(cx); + } + } + Poll::Pending + } +} diff --git a/protocols/autonatv2/src/server/handler.rs b/protocols/autonatv2/src/server/handler.rs new file mode 100644 index 00000000000..7beb8f00ec3 --- /dev/null +++ b/protocols/autonatv2/src/server/handler.rs @@ -0,0 +1,6 @@ +use either::Either; + +pub(crate) mod dial_back; +pub(crate) mod dial_request; + +pub type Handler = Either>; diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs new file mode 100644 index 00000000000..4f8d7915063 --- /dev/null +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -0,0 +1,139 @@ +use std::{ + collections::VecDeque, + convert::identity, + io, + task::{Context, Poll}, + time::Duration, +}; + +use futures::{AsyncWrite, AsyncWriteExt}; +use futures_bounded::FuturesSet; +use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; +use libp2p_swarm::{ + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; + +use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; + +use super::dial_request::{DialBack as DialBackRes, DialBackCommand}; + +pub type ToBehaviour = io::Result<()>; +pub type FromBehaviour = DialBackCommand; + +pub struct Handler { + pending_nonce: VecDeque, + requested_substream_nonce: VecDeque, + outbound: FuturesSet, +} + +impl Handler { + pub(crate) fn new() -> Self { + Self { + pending_nonce: VecDeque::new(), + requested_substream_nonce: VecDeque::new(), + outbound: FuturesSet::new(Duration::from_secs(10), 2), + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = FromBehaviour; + type ToBehaviour = ToBehaviour; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = ReadyUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Poll::Ready(result) = self.outbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + result + .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) + .and_then(identity), + )); + } + if let Some(cmd) = self.pending_nonce.pop_front() { + self.requested_substream_nonce.push_back(cmd); + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(DIAL_BACK_UPGRADE, ()), + }); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + self.pending_nonce.push_back(event); + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => { + if let Some(cmd) = self.requested_substream_nonce.pop_front() { + if self + .outbound + .try_push(perform_dial_back(protocol, cmd)) + .is_err() + { + tracing::warn!("Dial back dropped, too many requests in flight"); + } + } else { + tracing::warn!("received dial back substream without nonce"); + } + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + tracing::debug!("Dial back failed: {:?}", error); + } + _ => {} + } + todo!() + } +} + +async fn perform_dial_back( + mut stream: impl AsyncWrite + Unpin, + DialBackCommand { + nonce, + back_channel, + .. + }: DialBackCommand, +) -> io::Result<()> { + let res = perform_dial_back_inner(&mut stream, nonce) + .await + .map_err(|_| DialBackRes::DialBack) + .map(|_| DialBackRes::Ok) + .unwrap_or_else(|e| e); + back_channel + .send(res) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; + Ok(()) +} + +async fn perform_dial_back_inner( + mut stream: impl AsyncWrite + Unpin, + nonce: Nonce, +) -> io::Result<()> { + let dial_back = DialBack { nonce }; + dial_back.write_into(&mut stream).await?; + stream.close().await?; + Ok(()) +} diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs new file mode 100644 index 00000000000..f80a1385a6a --- /dev/null +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -0,0 +1,264 @@ +use std::{ + convert::identity, + io, + task::{Context, Poll}, + time::Duration, +}; + +use futures::{ + channel::{mpsc, oneshot}, + AsyncRead, AsyncWrite, SinkExt, StreamExt, +}; +use futures_bounded::FuturesSet; +use libp2p_core::{ + upgrade::{DeniedUpgrade, ReadyUpgrade}, + Multiaddr, +}; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use rand_core::RngCore; + +use crate::{ + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + request_response::{ + DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + }, + Nonce, REQUEST_UPGRADE, +}; + +#[derive(Debug, PartialEq)] +pub(crate) enum DialBack { + Dial, + DialBack, + Ok, +} + +#[derive(Debug)] +pub struct DialBackCommand { + pub(crate) addr: Multiaddr, + pub(crate) nonce: Nonce, + pub(crate) back_channel: oneshot::Sender, +} + +pub struct Handler { + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + dial_back_cmd_receiver: mpsc::Receiver, + inbound: FuturesSet>, + rng: R, +} + +impl Handler +where + R: RngCore, +{ + pub(crate) fn new(observed_multiaddr: Multiaddr, rng: R) -> Self { + let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); + Self { + observed_multiaddr, + dial_back_cmd_sender, + dial_back_cmd_receiver, + inbound: FuturesSet::new(Duration::from_secs(10), 2), + rng, + } + } +} + +impl ConnectionHandler for Handler +where + R: RngCore + Send + Clone + 'static, +{ + type FromBehaviour = (); + + type ToBehaviour = io::Result; + + type InboundProtocol = ReadyUpgrade; + + type OutboundProtocol = DeniedUpgrade; + + type InboundOpenInfo = (); + + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(REQUEST_UPGRADE, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(Err(e))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err(e))); + } + Poll::Ready(Err(e)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err( + io::Error::new(io::ErrorKind::TimedOut, e), + ))); + } + Poll::Ready(Ok(Ok(_))) => {} + Poll::Pending => {} + } + if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + if self + .inbound + .try_push(handle_request( + protocol, + self.observed_multiaddr.clone(), + self.dial_back_cmd_sender.clone(), + self.rng.clone(), + )) + .is_err() + { + tracing::warn!( + "failed to push inbound request handler, too many requests in flight" + ); + } + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { + tracing::debug!("inbound request failed: {:?}", error); + } + _ => {} + } + } +} + +enum HandleFail { + InternalError(usize), + RequestRejected, + DialRefused, + DialBack { idx: usize, err: DialBack }, +} + +impl From for DialResponse { + fn from(value: HandleFail) -> Self { + match value { + HandleFail::InternalError(addr_idx) => Self { + status: ResponseStatus::E_INTERNAL_ERROR, + addr_idx, + dial_status: DialStatus::UNUSED, + }, + HandleFail::RequestRejected => Self { + status: ResponseStatus::E_REQUEST_REJECTED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + }, + HandleFail::DialRefused => Self { + status: ResponseStatus::E_DIAL_REFUSED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + }, + HandleFail::DialBack { idx, err } => Self { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: match err { + DialBack::Dial => DialStatus::E_DIAL_ERROR, + DialBack::DialBack => DialStatus::E_DIAL_BACK_ERROR, + DialBack::Ok => DialStatus::OK, + }, + }, + } + } +} + +async fn handle_request_internal( + mut stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + mut rng: impl RngCore, +) -> Result { + let DialRequest { addrs, nonce } = match Request::read_from(&mut stream) + .await + .map_err(|_| HandleFail::InternalError(0))? + { + Request::Dial(dial_request) => dial_request, + Request::Data(_) => { + return Err(HandleFail::RequestRejected); + } + }; + for (idx, addr) in addrs.into_iter().enumerate() { + if addr != observed_multiaddr { + let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); + let mut rem_data = dial_data_request.num_bytes; + Response::Data(dial_data_request) + .write_into(&mut stream) + .await + .map_err(|_| HandleFail::InternalError(idx))?; + while rem_data > 0 { + let DialDataResponse { data_count } = match Request::read_from(&mut stream) + .await + .map_err(|_| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response, + }; + rem_data = rem_data.saturating_sub(data_count); + } + } + let (back_channel, rx) = oneshot::channel(); + let dial_back_cmd = DialBackCommand { + addr, + nonce, + back_channel, + }; + dial_back_cmd_sender + .clone() + .send(dial_back_cmd) + .await + .map_err(|_| HandleFail::InternalError(idx))?; + let dial_back = rx.await.map_err(|_| HandleFail::InternalError(idx))?; + if dial_back != DialBack::Ok { + return Err(HandleFail::DialBack { + idx, + err: dial_back, + }); + } + return Ok(DialResponse { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: DialStatus::OK, + }); + } + Err(HandleFail::DialRefused) +} + +async fn handle_request( + mut stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + rng: impl RngCore, +) -> io::Result<()> { + let response = + handle_request_internal(&mut stream, observed_multiaddr, dial_back_cmd_sender, rng) + .await + .unwrap_or_else(|e| e.into()); + Response::Dial(response).write_into(&mut stream).await?; + Ok(()) +} From 50fb5e4ab8cf06335fdc19dd285622c9acd3f2e8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:29:55 +0100 Subject: [PATCH 11/97] Pass the basic test for the first time. --- Cargo.lock | 11 +- protocols/autonatv2/Cargo.toml | 8 +- protocols/autonatv2/src/client.rs | 2 + protocols/autonatv2/src/client/behaviour.rs | 67 +++++++++--- protocols/autonatv2/src/client/handler.rs | 8 +- .../autonatv2/src/client/handler/dial_back.rs | 2 +- .../src/client/handler/dial_request.rs | 21 ++-- protocols/autonatv2/src/lib.rs | 4 +- protocols/autonatv2/src/server.rs | 2 + protocols/autonatv2/src/server/behaviour.rs | 15 ++- .../autonatv2/src/server/handler/dial_back.rs | 3 +- .../src/server/handler/dial_request.rs | 18 ++- protocols/autonatv2/tests/autonatv2.rs | 103 ++++++++++++++++++ 13 files changed, 215 insertions(+), 49 deletions(-) create mode 100644 protocols/autonatv2/tests/autonatv2.rs diff --git a/Cargo.lock b/Cargo.lock index 66efb5211f2..4f5d739fca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2520,16 +2520,19 @@ dependencies = [ "futures", "futures-bounded", "libp2p-core", + "libp2p-identify", "libp2p-identity", "libp2p-swarm", + "libp2p-swarm-test", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", "rand_core 0.6.4", - "scc", "static_assertions", "thiserror", + "tokio", "tracing", + "tracing-subscriber", "void", ] @@ -5072,12 +5075,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scc" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca628bbcc4be16ffaeae429444cf4538d7a23b1aa5457cc9ce9a220286befbc3" - [[package]] name = "schannel" version = "0.1.22" diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 8729108ab08..7a38ec38313 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -21,11 +21,17 @@ void = "1.0.2" either = "1.9.0" futures = "0.3.29" thiserror = "1.0.50" -scc = "2.0.3" bytes = "1" static_assertions = "1.1.0" tracing = "0.1.40" +[dev-dependencies] +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +libp2p-swarm-test = { workspace = true } +libp2p-identify = { workspace = true } +libp2p-swarm = { workspace = true, features = ["macros"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"]} + [lints] workspace = true diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index dff2397252c..be1f6fd8df0 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,2 +1,4 @@ mod behaviour; mod handler; + +pub use behaviour::Behaviour; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index d82e4eac17b..9dae7b31ea6 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; use rand::{distributions::Standard, seq::SliceRandom, Rng}; -use rand_core::RngCore; +use rand_core::{OsRng, RngCore}; use crate::{global_only::IpExt, request_response::DialRequest}; @@ -36,12 +36,21 @@ impl IntervalTicker { } } -pub(crate) struct Config { +pub struct Config { pub(crate) test_server_count: usize, pub(crate) max_addrs_count: usize, } -pub(crate) struct Behaviour +impl Default for Config { + fn default() -> Self { + Self { + test_server_count: 3, + max_addrs_count: 10, + } + } +} + +pub struct Behaviour where R: RngCore + 'static, { @@ -79,7 +88,7 @@ where if addr_is_local(remote_addr) { self.local_peers.insert(connection_id); } - Ok(Either::Left(dial_request::Handler::new())) + Ok(Either::Right(dial_back::Handler::new())) } fn handle_established_outbound_connection( @@ -93,7 +102,7 @@ where if addr_is_local(addr) { self.local_peers.insert(connection_id); } - Ok(Either::Right(dial_back::Handler::new())) + Ok(Either::Left(dial_request::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -139,9 +148,11 @@ where connection_id: ConnectionId, event: ::ToBehaviour, ) { - self.peers_to_handlers - .entry(peer_id) - .or_insert(connection_id); + if matches!(event, Either::Left(_)) { + self.peers_to_handlers + .entry(peer_id) + .or_insert(connection_id); + } match event { Either::Right(Ok(nonce)) => { if self.pending_nonces.remove(&nonce) { @@ -184,12 +195,12 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } - Either::Left(dial_request::ToBehaviour::TestCompleted( - Err(dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }) - )) - | Either::Left(dial_request::ToBehaviour::TestCompleted( - Err(dial_request::Error::FailureDuringDialBack { addr: Some(addr) }) - )) => { + Either::Left(dial_request::ToBehaviour::TestCompleted(Err( + dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }, + ))) + | Either::Left(dial_request::ToBehaviour::TestCompleted(Err( + dial_request::Error::FailureDuringDialBack { addr: Some(addr) }, + ))) => { self.pending_events .push_back(ToSwarm::ExternalAddrExpired(addr)); } @@ -207,7 +218,7 @@ where if pending_event.is_ready() { return pending_event; } - if self.ticker.ready() && !self.known_servers.is_empty() { + if self.ticker.ready() && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { let mut entries = self.address_candidates.drain().collect::>(); entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries @@ -246,6 +257,23 @@ impl Behaviour where R: RngCore + 'static, { + pub fn new(rng: R, config: Config) -> Self { + Self { + local_peers: HashSet::new(), + pending_nonces: HashSet::new(), + known_servers: Vec::new(), + rng, + config, + pending_events: VecDeque::new(), + address_candidates: HashMap::new(), + peers_to_handlers: HashMap::new(), + ticker: IntervalTicker { + interval: Duration::from_secs(0), + last_tick: Instant::now(), + }, + } + } + fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest) { if let Some(conn_id) = self.peers_to_handlers.get(&peer) { self.pending_events.push_back(ToSwarm::NotifyHandler { @@ -253,6 +281,9 @@ where handler: NotifyHandler::One(*conn_id), event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), }); + if self.pending_events.is_empty() { + println!("is empty") + } } else { tracing::debug!( "There should be a connection to {:?}, but there isn't", @@ -281,6 +312,12 @@ where } } +impl Default for Behaviour { + fn default() -> Self { + Self::new(OsRng, Config::default()) + } +} + fn addr_is_local(addr: &Multiaddr) -> bool { addr.iter().any(|c| match c { Protocol::Dns(addr) diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 9e8389bb630..f156d7b30c7 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -6,15 +6,15 @@ // TODO: tests // TODO: Handlers -pub(super) mod dial_back; -pub(super) mod dial_request; +pub mod dial_back; +pub mod dial_request; use either::Either; use std::time::Duration; pub(crate) use dial_request::TestEnd; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10000); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub(crate) type Handler = Either; +pub type Handler = Either; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index f4505335f2c..e883a45715e 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -19,7 +19,7 @@ use super::DEFAULT_TIMEOUT; pub(crate) type ToBehaviour = io::Result; -pub(crate) struct Handler { +pub struct Handler { inbound: FuturesSet>, } diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index a26fe4e458d..9a0ceecec12 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,4 +1,4 @@ -use futures::{channel::oneshot, AsyncRead, AsyncWrite}; +use futures::{channel::oneshot, AsyncRead, AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -13,7 +13,6 @@ use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, SubstreamProtocol, }; -use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; use std::{ collections::VecDeque, convert::identity, @@ -64,24 +63,24 @@ pub(crate) enum Error { } #[derive(Debug)] -pub(crate) struct TestEnd { +pub struct TestEnd { pub(crate) dial_request: DialRequest, pub(crate) suspicious_addr: Vec, pub(crate) reachable_addr: Multiaddr, } #[derive(Debug)] -pub(crate) enum ToBehaviour { +pub enum ToBehaviour { TestCompleted(Result), PeerHasServerSupport, } #[derive(Debug)] -pub(crate) enum FromBehaviour { +pub enum FromBehaviour { PerformRequest(DialRequest), } -pub(crate) struct Handler { +pub struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< ::OutboundProtocol, @@ -104,7 +103,7 @@ impl Handler { pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), + outbound: FuturesSet::new(DEFAULT_TIMEOUT, 10), queued_streams: VecDeque::default(), } } @@ -207,9 +206,10 @@ impl ConnectionHandler for Handler { }, ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { - self.queued_events.push_back( - ConnectionHandlerEvent::NotifyBehaviour(ToBehaviour::PeerHasServerSupport) - ); + self.queued_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::PeerHasServerSupport, + )); } } _ => {} @@ -281,6 +281,7 @@ async fn handle_substream( send_aap_data(&mut substream, num_bytes).await?; } Response::Dial(dial_response) => { + substream.close().await?; return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); } } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index 5e357b5cf0e..e64b9d480d4 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -1,8 +1,8 @@ use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; -mod client; -mod server; +pub mod client; +pub mod server; mod generated; mod global_only; pub(crate) mod request_response; diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs index dff2397252c..be1f6fd8df0 100644 --- a/protocols/autonatv2/src/server.rs +++ b/protocols/autonatv2/src/server.rs @@ -1,2 +1,4 @@ mod behaviour; mod handler; + +pub use behaviour::Behaviour; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index a96d412a0b7..0771e83c13f 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -11,7 +11,8 @@ use libp2p_swarm::{ ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, }; -use rand_core::RngCore; +use rand_core::{OsRng, RngCore}; +use libp2p_core::multiaddr::Protocol; use super::handler::{ dial_back, @@ -19,7 +20,7 @@ use super::handler::{ Handler, }; -pub struct Behaviour +pub struct Behaviour where R: Clone + Send + RngCore + 'static, { @@ -35,11 +36,17 @@ where rng: R, } +impl Default for Behaviour { + fn default() -> Self { + Self::new(OsRng) + } +} + impl Behaviour where R: RngCore + Send + Clone + 'static, { - pub(crate) fn new(rng: R) -> Self { + pub fn new(rng: R) -> Self { Self { handlers: HashMap::new(), pending_dial_back: HashMap::new(), @@ -95,7 +102,7 @@ where port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { if port_use == PortUse::New { - self.handlers.insert((addr.clone(), peer), connection_id); + self.handlers.insert((addr.iter().filter(|e| !matches!(e, Protocol::P2p(_))).collect(), peer), connection_id); } Ok(Either::Left(dial_back::Handler::new())) } diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 4f8d7915063..45a3fba8480 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -32,7 +32,7 @@ impl Handler { Self { pending_nonce: VecDeque::new(), requested_substream_nonce: VecDeque::new(), - outbound: FuturesSet::new(Duration::from_secs(10), 2), + outbound: FuturesSet::new(Duration::from_secs(10000), 2), } } } @@ -105,7 +105,6 @@ impl ConnectionHandler for Handler { } _ => {} } - todo!() } } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index f80a1385a6a..554675f5b20 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -7,8 +7,9 @@ use std::{ use futures::{ channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, SinkExt, StreamExt, + AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, }; +use futures::future::FusedFuture; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -60,7 +61,7 @@ where observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, - inbound: FuturesSet::new(Duration::from_secs(10), 2), + inbound: FuturesSet::new(Duration::from_secs(1000), 2), rng, } } @@ -105,6 +106,7 @@ where Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); } Poll::Pending @@ -146,6 +148,10 @@ where _ => {} } } + + fn connection_keep_alive(&self) -> bool { + true + } } enum HandleFail { @@ -233,7 +239,12 @@ async fn handle_request_internal( .send(dial_back_cmd) .await .map_err(|_| HandleFail::InternalError(idx))?; - let dial_back = rx.await.map_err(|_| HandleFail::InternalError(idx))?; + if rx.is_terminated() { + println!("is terminated"); + } + let dial_back = rx.await.map_err(|e| { + HandleFail::InternalError(idx) + })?; if dial_back != DialBack::Ok { return Err(HandleFail::DialBack { idx, @@ -260,5 +271,6 @@ async fn handle_request( .await .unwrap_or_else(|e| e.into()); Response::Dial(response).write_into(&mut stream).await?; + stream.close().await?; Ok(()) } diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs new file mode 100644 index 00000000000..c3acdd3c462 --- /dev/null +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -0,0 +1,103 @@ +use std::task::Poll; + +use futures::StreamExt; +use libp2p_swarm::{Swarm, SwarmEvent, ToSwarm}; +use libp2p_swarm_test::SwarmExt; +use tracing_subscriber::EnvFilter; + +#[tokio::test] +async fn foo() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let mut alice = new_server().await; + let cor_server_peer = alice.local_peer_id().clone(); + let mut bob = new_client().await; + let cor_client_peer = bob.local_peer_id().clone(); + bob.connect(&mut alice).await; + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + peer_id: client_peer_sent, + }), CombinedServerEvent::Identify(libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + })], + [CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + peer_id: server_peer_sent, + }), CombinedClientEvent::Identify(libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + })], + ) => { + assert_eq!(server_peer_sent, cor_server_peer); + assert_eq!(client_peer_sent, cor_client_peer); + assert_eq!(server_peer_recv, cor_server_peer); + assert_eq!(client_peer_recv, cor_client_peer); + } + e => panic!("unexpected event: {:?}", e), + } + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [ + SwarmEvent::Dialing { .. }, + SwarmEvent::ConnectionEstablished { .. }, + SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), + SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), + SwarmEvent::NewExternalAddrCandidate { ..}, + ],[ + SwarmEvent::NewExternalAddrCandidate { address: addr_new }, + SwarmEvent::IncomingConnection { .. }, + SwarmEvent::ConnectionEstablished { .. }, + SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), + SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), + SwarmEvent::NewExternalAddrCandidate { address: addr_snd}, + SwarmEvent::ExternalAddrConfirmed { address: addr_ok} + ] + ) => { + assert_eq!(addr_new, addr_snd); + assert_eq!(addr_snd, addr_ok); + } + _ => todo!() + } + +} + +async fn new_server() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| CombinedServer { + autonat: libp2p_autonatv2::server::Behaviour::default(), + identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )), + }); + node.listen().with_tcp_addr_external().await; + + node +} + +async fn new_client() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| CombinedClient { + autonat: libp2p_autonatv2::client::Behaviour::default(), + identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )), + }); + node.listen().with_tcp_addr_external().await; + node +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct CombinedServer { + autonat: libp2p_autonatv2::server::Behaviour, + identify: libp2p_identify::Behaviour, +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct CombinedClient { + autonat: libp2p_autonatv2::client::Behaviour, + identify: libp2p_identify::Behaviour, +} From d34782e91999993759f640a8e6f2e4406649bb35 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 6 Dec 2023 21:22:48 +0100 Subject: [PATCH 12/97] Move forwards to fewer bug fixes --- Cargo.lock | 3 + protocols/autonatv2/Cargo.toml | 1 + protocols/autonatv2/src/client/behaviour.rs | 20 ++- protocols/autonatv2/src/client/handler.rs | 8 +- .../autonatv2/src/client/handler/dial_back.rs | 11 +- .../src/client/handler/dial_request.rs | 30 ++-- protocols/autonatv2/src/lib.rs | 2 +- protocols/autonatv2/src/request_response.rs | 140 ++++++++++++----- protocols/autonatv2/src/server/behaviour.rs | 60 ++++++-- protocols/autonatv2/src/server/handler.rs | 2 +- .../autonatv2/src/server/handler/dial_back.rs | 17 ++- .../src/server/handler/dial_request.rs | 61 ++++---- protocols/autonatv2/tests/autonatv2.rs | 141 ++++++++++++++---- 13 files changed, 356 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f5d739fca6..b67b765eff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2533,6 +2533,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "unsigned-varint 0.8.0", "void", ] @@ -6119,6 +6120,8 @@ checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" dependencies = [ "asynchronous-codec", "bytes", + "futures-io", + "futures-util", ] [[package]] diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 7a38ec38313..3f5ccf0b68e 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -24,6 +24,7 @@ thiserror = "1.0.50" bytes = "1" static_assertions = "1.1.0" tracing = "0.1.40" +unsigned-varint = { workspace = true, features = ["futures"] } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 9dae7b31ea6..4557d4da548 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -9,11 +9,10 @@ use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, - dial_opts::{DialOpts, PeerCondition}, ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; -use rand::{distributions::Standard, seq::SliceRandom, Rng}; +use rand::{seq::SliceRandom, Rng}; use rand_core::{OsRng, RngCore}; use crate::{global_only::IpExt, request_response::DialRequest}; @@ -66,6 +65,7 @@ where >, >, address_candidates: HashMap, + already_tested: HashSet, peers_to_handlers: HashMap, ticker: IntervalTicker, } @@ -108,7 +108,10 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - *self.address_candidates.entry(addr.clone()).or_default() += 1; + if !self.already_tested.contains(addr) { + println!("external addr: {addr}"); + *self.address_candidates.entry(addr.clone()).or_default() += 1; + } } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { self.address_candidates.remove(addr); @@ -218,7 +221,10 @@ where if pending_event.is_ready() { return pending_event; } - if self.ticker.ready() && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { + if self.ticker.ready() + && !self.known_servers.is_empty() + && !self.address_candidates.is_empty() + { let mut entries = self.address_candidates.drain().collect::>(); entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries @@ -227,6 +233,7 @@ where .map(|(addr, _)| addr) .take(self.config.max_addrs_count) .collect::>(); + self.already_tested.extend(addrs.iter().cloned()); let peers = if self.known_servers.len() < self.config.test_server_count { self.known_servers.clone() } else { @@ -267,6 +274,7 @@ where pending_events: VecDeque::new(), address_candidates: HashMap::new(), peers_to_handlers: HashMap::new(), + already_tested: HashSet::new(), ticker: IntervalTicker { interval: Duration::from_secs(0), last_tick: Instant::now(), @@ -310,6 +318,10 @@ where } Poll::Pending } + + pub fn inject_test_addr(&mut self, addr: Multiaddr) { + *self.address_candidates.entry(addr).or_default() += 1; + } } impl Default for Behaviour { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index f156d7b30c7..e4a48992727 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -6,15 +6,15 @@ // TODO: tests // TODO: Handlers -pub mod dial_back; -pub mod dial_request; +pub(crate) mod dial_back; +pub(crate) mod dial_request; use either::Either; use std::time::Duration; pub(crate) use dial_request::TestEnd; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10000); +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub type Handler = Either; +pub(crate) type Handler = Either; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index e883a45715e..906ba6a35e8 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -4,9 +4,8 @@ use std::{ task::{Context, Poll}, }; -use either::Either; use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; -use futures_bounded::{FuturesSet, Timeout}; +use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, @@ -15,7 +14,7 @@ use libp2p_swarm::{ use crate::{request_response::DialBack, Nonce}; -use super::DEFAULT_TIMEOUT; +use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; pub(crate) type ToBehaviour = io::Result; @@ -26,7 +25,7 @@ pub struct Handler { impl Handler { pub(crate) fn new() -> Self { Self { - inbound: FuturesSet::new(DEFAULT_TIMEOUT, 2), + inbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), } } } @@ -84,6 +83,10 @@ impl ConnectionHandler for Handler { _ => {} } } + + fn connection_keep_alive(&self) -> bool { + false + } } async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index 9a0ceecec12..2545f92f063 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -21,6 +21,7 @@ use std::{ task::{Context, Poll}, }; +use crate::request_response::Coder; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ @@ -30,10 +31,10 @@ use crate::{ REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, }; -use super::DEFAULT_TIMEOUT; +use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; #[derive(Debug, thiserror::Error)] -pub(crate) enum Error { +pub enum Error { #[error("io error")] Io(#[from] io::Error), #[error("invalid referenced address index: {index} (max number of addr: {max})")] @@ -103,12 +104,13 @@ impl Handler { pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(DEFAULT_TIMEOUT, 10), + outbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), queued_streams: VecDeque::default(), } } fn perform_request(&mut self, req: DialRequest) { + println!("{req:?}"); let (tx, rx) = oneshot::channel(); self.queued_streams.push_back(tx); self.queued_events @@ -235,14 +237,15 @@ async fn start_substream_handle( async fn handle_substream( dial_request: DialRequest, - mut substream: impl AsyncRead + AsyncWrite + Unpin, + substream: impl AsyncRead + AsyncWrite + Unpin, ) -> Result { - Request::Dial(dial_request.clone()) - .write_into(&mut substream) + let mut coder = Coder::new(substream); + coder + .send_request(Request::Dial(dial_request.clone())) .await?; let mut suspicious_addr = Vec::new(); loop { - match Response::read_from(&mut substream).await? { + match coder.next_response().await? { Response::Data(DialDataRequest { addr_idx, num_bytes, @@ -278,10 +281,11 @@ async fn handle_substream( } } - send_aap_data(&mut substream, num_bytes).await?; + println!("Time to bpay the tribute"); + send_aap_data(&mut coder, num_bytes).await?; } Response::Dial(dial_response) => { - substream.close().await?; + coder.close().await?; return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); } } @@ -328,7 +332,10 @@ fn test_end_from_dial_response( } } -async fn send_aap_data(mut substream: impl AsyncWrite + Unpin, num_bytes: usize) -> io::Result<()> { +async fn send_aap_data(substream: &mut Coder, num_bytes: usize) -> io::Result<()> +where + I: AsyncWrite + Unpin, +{ let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) @@ -337,7 +344,8 @@ async fn send_aap_data(mut substream: impl AsyncWrite + Unpin, num_bytes: usize) .filter(|e| *e > 0) .map(|data_count| Request::Data(DialDataResponse { data_count })) { - req.write_into(&mut substream).await?; + println!("Data req: {req:?}"); + substream.send_request(req).await?; } Ok(()) } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index e64b9d480d4..2b4db326485 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -2,10 +2,10 @@ use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; pub mod client; -pub mod server; mod generated; mod global_only; pub(crate) mod request_response; +pub mod server; pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index b87852c7302..5716863dee6 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -1,10 +1,11 @@ // change to quick-protobuf-codec +use std::io::ErrorKind; use std::{borrow::Cow, io}; -use asynchronous_codec::{FramedRead, FramedWrite}; +use asynchronous_codec::{Framed, FramedRead, FramedWrite}; -use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; @@ -29,30 +30,82 @@ macro_rules! check_existence { }; } -#[derive(Debug, Clone)] +pub(crate) struct Coder { + inner: Framed>, +} + +impl Coder +where + I: AsyncWrite + AsyncRead + Unpin, +{ + pub(crate) fn new(io: I) -> Self { + Self { + inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), + } + } + pub(crate) async fn close(self) -> io::Result<()> { + let mut stream = self.inner.into_inner(); + stream.close().await?; + Ok(()) + } +} + +impl Coder +where + I: AsyncRead + Unpin, +{ + async fn next_msg(&mut self) -> io::Result { + self.inner + .next() + .await + .ok_or(io::Error::new(ErrorKind::Other, "no request to read"))? + .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) + } + pub(crate) async fn next_request(&mut self) -> io::Result { + Request::from_proto(self.next_msg().await?) + } + pub(crate) async fn next_response(&mut self) -> io::Result { + Response::from_proto(self.next_msg().await?) + } +} + +impl Coder +where + I: AsyncWrite + Unpin, +{ + async fn send_msg(&mut self, msg: proto::Message) -> io::Result<()> { + self.inner.send(msg).await?; + Ok(()) + } + + pub(crate) async fn send_request(&mut self, request: Request) -> io::Result<()> { + self.send_msg(request.into_proto()).await + } + + pub(crate) async fn send_response(&mut self, response: Response) -> io::Result<()> { + self.send_msg(response.into_proto()).await + } +} + +#[derive(Debug, Clone, PartialEq)] pub(crate) enum Request { Dial(DialRequest), Data(DialDataResponse), } -#[derive(Debug, Clone)] -pub(crate) struct DialRequest { +#[derive(Debug, Clone, PartialEq)] +pub struct DialRequest { pub(crate) addrs: Vec, pub(crate) nonce: u64, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct DialDataResponse { pub(crate) data_count: usize, } impl Request { - pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { - let mut framed_io = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)); - let msg = framed_io - .next() - .await - .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; + pub(crate) fn from_proto(msg: proto::Message) -> io::Result { match msg.msg { proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { let addrs: Vec = addrs @@ -77,8 +130,8 @@ impl Request { } } - pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = match self { + pub(crate) fn into_proto(self) -> proto::Message { + match self { Request::Dial(DialRequest { addrs, nonce }) => { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); let nonce = Some(nonce); @@ -101,11 +154,7 @@ impl Request { }), } } - }; - FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + } } } @@ -129,12 +178,7 @@ pub(crate) struct DialResponse { } impl Response { - pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> std::io::Result { - let msg = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)) - .next() - .await - .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; - + pub(crate) fn from_proto(msg: proto::Message) -> std::io::Result { match msg.msg { proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { status, @@ -167,8 +211,8 @@ impl Response { } } - pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = match self { + pub(crate) fn into_proto(self) -> proto::Message { + match self { Self::Dial(DialResponse { status, addr_idx, @@ -189,11 +233,7 @@ impl Response { numBytes: Some(num_bytes as u64), }), }, - }; - FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + } } } @@ -205,11 +245,6 @@ impl DialDataRequest { num_bytes, } } - - #[cfg(any(doc, feature = "rand"))] - pub(crate) fn new(addr_idx: usize) -> Self { - Self::from_rng(addr_idx, rand::thread_rng()) - } } const DIAL_BACK_MAX_SIZE: usize = 10; @@ -242,12 +277,18 @@ impl DialBack { #[cfg(test)] mod tests { - use crate::generated::structs::{mod_Message::OneOfmsg, DialDataResponse, Message}; + use crate::generated::structs::{ + mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, + }; + use crate::request_response::{Coder, DialDataResponse, Request}; + use futures::io::Cursor; + + use rand::{thread_rng, Rng}; #[test] fn message_correct_max_size() { let message_bytes = quick_protobuf::serialize_into_vec(&Message { - msg: OneOfmsg::dialDataResponse(DialDataResponse { + msg: OneOfmsg::dialDataResponse(GenDialDataResponse { data: Some(vec![0; 4096].into()), }), }) @@ -271,4 +312,25 @@ mod tests { let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); } + + #[tokio::test] + async fn write_read_request() { + let mut buf = Cursor::new(Vec::new()); + let mut coder = Coder::new(&mut buf); + let mut all_req = Vec::with_capacity(100); + for _ in 0..100 { + let data_request: Request = Request::Data(DialDataResponse { + data_count: thread_rng().gen_range(0..4000), + }); + all_req.push(data_request.clone()); + coder.send_request(data_request.clone()).await.unwrap(); + } + let inner = coder.inner.into_inner(); + inner.set_position(0); + let mut coder = Coder::new(inner); + for i in 0..100 { + let read_data_request = coder.next_request().await.unwrap(); + assert_eq!(read_data_request, all_req[i]); + } + } } diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 0771e83c13f..e07f3cd29d3 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -3,16 +3,16 @@ use std::{ task::{Context, Poll}, }; +use crate::server::handler::dial_request::DialBack; use either::Either; +use libp2p_core::multiaddr::Protocol; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - dial_opts::{DialOpts, PeerCondition}, - ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NotifyHandler, - ToSwarm, + dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialError, DialFailure, + FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, }; use rand_core::{OsRng, RngCore}; -use libp2p_core::multiaddr::Protocol; use super::handler::{ dial_back, @@ -25,7 +25,6 @@ where R: Clone + Send + RngCore + 'static, { handlers: HashMap<(Multiaddr, PeerId), ConnectionId>, - pending_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, dialing_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, pending_events: VecDeque< ToSwarm< @@ -49,7 +48,6 @@ where pub fn new(rng: R) -> Self { Self { handlers: HashMap::new(), - pending_dial_back: HashMap::new(), dialing_dial_back: HashMap::new(), pending_events: VecDeque::new(), rng, @@ -58,7 +56,7 @@ where fn poll_pending_events( &mut self, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll< ToSwarm< ::ToSwarm, @@ -98,16 +96,47 @@ where connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, - role_override: Endpoint, + _role_override: Endpoint, port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { if port_use == PortUse::New { - self.handlers.insert((addr.iter().filter(|e| !matches!(e, Protocol::P2p(_))).collect(), peer), connection_id); + self.handlers.insert( + ( + addr.iter() + .filter(|e| !matches!(e, Protocol::P2p(_))) + .collect(), + peer, + ), + connection_id, + ); + println!("Found handler"); } Ok(Either::Left(dial_back::Handler::new())) } - fn on_swarm_event(&mut self, event: FromSwarm) {} + fn on_swarm_event(&mut self, event: FromSwarm) { + println!("EVENT: {event:?}"); + match event { + FromSwarm::DialFailure(DialFailure { + error: DialError::Transport(pairs), + .. + }) => { + println!("Failed to dial"); + for (addr, _) in pairs.iter() { + if let Some((_, p)) = self.dialing_dial_back.keys().find(|(a, _)| a == addr) { + let cmds = self.dialing_dial_back.remove(&(addr.clone(), *p)).unwrap(); + for cmd in cmds { + let _ = cmd.back_channel.send(DialBack::Dial); + } + } + } + } + FromSwarm::DialFailure(m) => { + println!("{m:?}"); + } + _ => {} + } + } fn on_connection_handler_event( &mut self, @@ -132,9 +161,14 @@ where pending.push_back(cmd); } else { self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer_id.clone()) - .condition(PeerCondition::Always) - .addresses(vec![addr.clone()]) + /* opts: DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr.clone()])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(), + */ + opts: DialOpts::unknown_peer_id() + .address(addr.clone()) .allocate_new_port() .build(), }); diff --git a/protocols/autonatv2/src/server/handler.rs b/protocols/autonatv2/src/server/handler.rs index 7beb8f00ec3..764e1a71f1f 100644 --- a/protocols/autonatv2/src/server/handler.rs +++ b/protocols/autonatv2/src/server/handler.rs @@ -3,4 +3,4 @@ use either::Either; pub(crate) mod dial_back; pub(crate) mod dial_request; -pub type Handler = Either>; +pub(crate) type Handler = Either>; diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 45a3fba8480..8a1d06a74a2 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -10,16 +10,16 @@ use futures::{AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; +use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME, DIAL_BACK_UPGRADE}; use super::dial_request::{DialBack as DialBackRes, DialBackCommand}; -pub type ToBehaviour = io::Result<()>; -pub type FromBehaviour = DialBackCommand; +pub(crate) type ToBehaviour = io::Result<()>; +pub(crate) type FromBehaviour = DialBackCommand; pub struct Handler { pending_nonce: VecDeque, @@ -103,7 +103,14 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { tracing::debug!("Dial back failed: {:?}", error); } - _ => {} + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut protocols)) => { + if !protocols.any(|p| p == &DIAL_BACK_PROTOCOL_NAME) { + todo!("handle when dialed back but not supporting protocol"); + } + } + e => { + println!("e: {:?}", e); + } } } } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 554675f5b20..4405e8ce7c0 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -1,15 +1,14 @@ use std::{ - convert::identity, io, task::{Context, Poll}, time::Duration, }; +use futures::future::FusedFuture; use futures::{ channel::{mpsc, oneshot}, AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, }; -use futures::future::FusedFuture; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -21,6 +20,7 @@ use libp2p_swarm::{ }; use rand_core::RngCore; +use crate::request_response::Coder; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ @@ -31,6 +31,7 @@ use crate::{ #[derive(Debug, PartialEq)] pub(crate) enum DialBack { + #[allow(unused)] Dial, DialBack, Ok, @@ -61,7 +62,7 @@ where observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, - inbound: FuturesSet::new(Duration::from_secs(1000), 2), + inbound: FuturesSet::new(Duration::from_secs(10), 10), rng, } } @@ -106,7 +107,6 @@ where Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); } Poll::Pending @@ -192,13 +192,17 @@ impl From for DialResponse { } } -async fn handle_request_internal( - mut stream: impl AsyncRead + AsyncWrite + Unpin, +async fn handle_request_internal( + coder: &mut Coder, observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, mut rng: impl RngCore, -) -> Result { - let DialRequest { addrs, nonce } = match Request::read_from(&mut stream) +) -> Result +where + I: AsyncRead + AsyncWrite + Unpin, +{ + let DialRequest { addrs, nonce } = match coder + .next_request() .await .map_err(|_| HandleFail::InternalError(0))? { @@ -207,24 +211,29 @@ async fn handle_request_internal( return Err(HandleFail::RequestRejected); } }; + println!("incoming addr: {addrs:?}"); for (idx, addr) in addrs.into_iter().enumerate() { if addr != observed_multiaddr { let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); let mut rem_data = dial_data_request.num_bytes; - Response::Data(dial_data_request) - .write_into(&mut stream) + coder + .send_response(Response::Data(dial_data_request)) .await .map_err(|_| HandleFail::InternalError(idx))?; while rem_data > 0 { - let DialDataResponse { data_count } = match Request::read_from(&mut stream) - .await - .map_err(|_| HandleFail::InternalError(idx))? - { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => dial_data_response, - }; + let DialDataResponse { data_count } = + match coder.next_request().await.map_err(|e| { + println!("err: {e:?}"); + HandleFail::InternalError(idx) + })? { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => { + println!("Dial data response: {dial_data_response:?}"); + dial_data_response + } + }; rem_data = rem_data.saturating_sub(data_count); } } @@ -242,9 +251,7 @@ async fn handle_request_internal( if rx.is_terminated() { println!("is terminated"); } - let dial_back = rx.await.map_err(|e| { - HandleFail::InternalError(idx) - })?; + let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; if dial_back != DialBack::Ok { return Err(HandleFail::DialBack { idx, @@ -261,16 +268,18 @@ async fn handle_request_internal( } async fn handle_request( - mut stream: impl AsyncRead + AsyncWrite + Unpin, + stream: impl AsyncRead + AsyncWrite + Unpin, observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, rng: impl RngCore, ) -> io::Result<()> { + let mut coder = Coder::new(stream); let response = - handle_request_internal(&mut stream, observed_multiaddr, dial_back_cmd_sender, rng) + handle_request_internal(&mut coder, observed_multiaddr, dial_back_cmd_sender, rng) .await .unwrap_or_else(|e| e.into()); - Response::Dial(response).write_into(&mut stream).await?; - stream.close().await?; + println!("Response: {response:?}"); + coder.send_response(Response::Dial(response)).await?; + coder.close().await?; Ok(()) } diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index c3acdd3c462..dbc5e2d1819 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -1,12 +1,59 @@ -use std::task::Poll; - -use futures::StreamExt; -use libp2p_swarm::{Swarm, SwarmEvent, ToSwarm}; +use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt; use tracing_subscriber::EnvFilter; #[tokio::test] -async fn foo() { +async fn confirm_successful() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let mut alice = new_server().await; + let cor_server_peer = alice.local_peer_id().clone(); + let mut bob = new_client().await; + let cor_client_peer = bob.local_peer_id().clone(); + bob.connect(&mut alice).await; + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + peer_id: client_peer_sent, + })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( + libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + }, + )), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + peer_id: server_peer_sent, + })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( + libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + }, + ))], + ) => { + assert_eq!(server_peer_sent, cor_server_peer); + assert_eq!(client_peer_sent, cor_client_peer); + assert_eq!(server_peer_recv, cor_server_peer); + assert_eq!(client_peer_recv, cor_client_peer); + } + e => panic!("unexpected events: {e:#?}"), + } + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], + ) => { + assert_eq!(addr_new, addr_snd); + assert_eq!(addr_snd, addr_ok); + } + e => panic!("unknown events {e:#?}"), + } +} + +#[tokio::test] +async fn dial_back_to_not_supporting() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); @@ -15,52 +62,71 @@ async fn foo() { let mut bob = new_client().await; let cor_client_peer = bob.local_peer_id().clone(); bob.connect(&mut alice).await; + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( - [CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { peer_id: client_peer_sent, - }), CombinedServerEvent::Identify(libp2p_identify::Event::Received { - peer_id: client_peer_recv, - .. - })], - [CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( + libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + }, + )), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { peer_id: server_peer_sent, - }), CombinedClientEvent::Identify(libp2p_identify::Event::Received { - peer_id: server_peer_recv, - .. - })], + })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( + libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + }, + ))], ) => { assert_eq!(server_peer_sent, cor_server_peer); assert_eq!(client_peer_sent, cor_client_peer); assert_eq!(server_peer_recv, cor_server_peer); assert_eq!(client_peer_recv, cor_client_peer); } - e => panic!("unexpected event: {:?}", e), + e => panic!("unexpected events: {e:#?}"), } + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( - [ - SwarmEvent::Dialing { .. }, - SwarmEvent::ConnectionEstablished { .. }, - SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), - SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), - SwarmEvent::NewExternalAddrCandidate { ..}, - ],[ - SwarmEvent::NewExternalAddrCandidate { address: addr_new }, - SwarmEvent::IncomingConnection { .. }, - SwarmEvent::ConnectionEstablished { .. }, - SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), - SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), - SwarmEvent::NewExternalAddrCandidate { address: addr_snd}, - SwarmEvent::ExternalAddrConfirmed { address: addr_ok} - ] + [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], ) => { assert_eq!(addr_new, addr_snd); assert_eq!(addr_snd, addr_ok); } - _ => todo!() + e => panic!("unknown events {e:#?}"), } + println!("Standard succeed"); + + let mut hannes = new_dummy().await; + let unreachable_address = hannes.external_addresses().next().unwrap().clone(); + bob.behaviour_mut() + .autonat + .inject_test_addr(unreachable_address.clone()); + + tokio::spawn(async move { + loop { + let hannes_event = hannes.next_swarm_event().await; + println!("Hannes: {hannes_event:?}"); + } + }); + + tokio::spawn(async move { + loop { + let bob_event = bob.next_swarm_event().await; + println!("Bob: {bob_event:?}"); + } + }); + + loop { + let alice_event = alice.next_swarm_event().await; + println!("Alice: {alice_event:?}"); + } } async fn new_server() -> Swarm { @@ -101,3 +167,14 @@ struct CombinedClient { autonat: libp2p_autonatv2::client::Behaviour, identify: libp2p_identify::Behaviour, } + +async fn new_dummy() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| { + libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )) + }); + node.listen().with_tcp_addr_external().await; + node +} From fee9de17017d40f0b34c00d3de873e8e032e9145 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:01:52 +0100 Subject: [PATCH 13/97] Make the things actually expire --- protocols/autonatv2/src/client/behaviour.rs | 4 -- .../src/client/handler/dial_request.rs | 3 -- protocols/autonatv2/src/server/behaviour.rs | 39 ++++++++++++------- .../autonatv2/src/server/handler/dial_back.rs | 23 ++++++----- .../src/server/handler/dial_request.rs | 28 +++++-------- protocols/autonatv2/tests/autonatv2.rs | 24 +++++------- 6 files changed, 58 insertions(+), 63 deletions(-) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 4557d4da548..70ecb4b3284 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -109,7 +109,6 @@ where match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { if !self.already_tested.contains(addr) { - println!("external addr: {addr}"); *self.address_candidates.entry(addr.clone()).or_default() += 1; } } @@ -289,9 +288,6 @@ where handler: NotifyHandler::One(*conn_id), event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), }); - if self.pending_events.is_empty() { - println!("is empty") - } } else { tracing::debug!( "There should be a connection to {:?}, but there isn't", diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index 2545f92f063..a4700f679d7 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -110,7 +110,6 @@ impl Handler { } fn perform_request(&mut self, req: DialRequest) { - println!("{req:?}"); let (tx, rx) = oneshot::channel(); self.queued_streams.push_back(tx); self.queued_events @@ -281,7 +280,6 @@ async fn handle_substream( } } - println!("Time to bpay the tribute"); send_aap_data(&mut coder, num_bytes).await?; } Response::Dial(dial_response) => { @@ -344,7 +342,6 @@ where .filter(|e| *e > 0) .map(|data_count| Request::Data(DialDataResponse { data_count })) { - println!("Data req: {req:?}"); substream.send_request(req).await?; } Ok(()) diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index e07f3cd29d3..3f20fbf1bfb 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::{ collections::{HashMap, VecDeque}, task::{Context, Poll}, @@ -8,6 +9,7 @@ use either::Either; use libp2p_core::multiaddr::Protocol; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialError, DialFailure, FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, @@ -109,19 +111,33 @@ where ), connection_id, ); - println!("Found handler"); } Ok(Either::Left(dial_back::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { - println!("EVENT: {event:?}"); match event { + FromSwarm::DialFailure(DialFailure { + error: DialError::WrongPeerId { .. }, + peer_id: Some(peer_id), + .. + }) => { + if let Some(key) = self + .dialing_dial_back + .keys() + .find(|(_, p)| *p == peer_id) + .cloned() + { + let cmds = self.dialing_dial_back.remove(&key).unwrap(); + for cmd in cmds { + let _ = cmd.back_channel.send(DialBack::Dial); + } + } + } FromSwarm::DialFailure(DialFailure { error: DialError::Transport(pairs), .. }) => { - println!("Failed to dial"); for (addr, _) in pairs.iter() { if let Some((_, p)) = self.dialing_dial_back.keys().find(|(a, _)| a == addr) { let cmds = self.dialing_dial_back.remove(&(addr.clone(), *p)).unwrap(); @@ -131,9 +147,6 @@ where } } } - FromSwarm::DialFailure(m) => { - println!("{m:?}"); - } _ => {} } } @@ -161,16 +174,16 @@ where pending.push_back(cmd); } else { self.pending_events.push_back(ToSwarm::Dial { - /* opts: DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr.clone()])) - .condition(PeerCondition::Always) + opts: DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr.clone()])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(), + /* opts: DialOpts::unknown_peer_id() + .address(addr.clone()) .allocate_new_port() .build(), */ - opts: DialOpts::unknown_peer_id() - .address(addr.clone()) - .allocate_new_port() - .build(), }); self.dialing_dial_back .insert((addr, peer_id), VecDeque::from([cmd])); diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 8a1d06a74a2..c282ff522dc 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -11,7 +11,8 @@ use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, + SubstreamProtocol, }; use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME, DIAL_BACK_UPGRADE}; @@ -100,17 +101,19 @@ impl ConnectionHandler for Handler { tracing::warn!("received dial back substream without nonce"); } } - ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { - tracing::debug!("Dial back failed: {:?}", error); - } - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut protocols)) => { - if !protocols.any(|p| p == &DIAL_BACK_PROTOCOL_NAME) { - todo!("handle when dialed back but not supporting protocol"); + ConnectionEvent::DialUpgradeError(DialUpgradeError { + error: StreamUpgradeError::NegotiationFailed, + .. + }) + | ConnectionEvent::DialUpgradeError(DialUpgradeError { + error: StreamUpgradeError::Timeout, + .. + }) => { + if let Some(cmd) = self.requested_substream_nonce.pop_front() { + let _ = cmd.back_channel.send(DialBackRes::DialBack); } } - e => { - println!("e: {:?}", e); - } + _ => {} } } } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 4405e8ce7c0..6a7cf89de0c 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -211,7 +211,6 @@ where return Err(HandleFail::RequestRejected); } }; - println!("incoming addr: {addrs:?}"); for (idx, addr) in addrs.into_iter().enumerate() { if addr != observed_multiaddr { let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); @@ -221,19 +220,16 @@ where .await .map_err(|_| HandleFail::InternalError(idx))?; while rem_data > 0 { - let DialDataResponse { data_count } = - match coder.next_request().await.map_err(|e| { - println!("err: {e:?}"); - HandleFail::InternalError(idx) - })? { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => { - println!("Dial data response: {dial_data_response:?}"); - dial_data_response - } - }; + let DialDataResponse { data_count } = match coder + .next_request() + .await + .map_err(|e| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response, + }; rem_data = rem_data.saturating_sub(data_count); } } @@ -248,9 +244,6 @@ where .send(dial_back_cmd) .await .map_err(|_| HandleFail::InternalError(idx))?; - if rx.is_terminated() { - println!("is terminated"); - } let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; if dial_back != DialBack::Ok { return Err(HandleFail::DialBack { @@ -278,7 +271,6 @@ async fn handle_request( handle_request_internal(&mut coder, observed_multiaddr, dial_back_cmd_sender, rng) .await .unwrap_or_else(|e| e.into()); - println!("Response: {response:?}"); coder.send_response(Response::Dial(response)).await?; coder.close().await?; Ok(()) diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index dbc5e2d1819..332918f249d 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -101,32 +101,26 @@ async fn dial_back_to_not_supporting() { e => panic!("unknown events {e:#?}"), } - println!("Standard succeed"); - let mut hannes = new_dummy().await; let unreachable_address = hannes.external_addresses().next().unwrap().clone(); bob.behaviour_mut() .autonat .inject_test_addr(unreachable_address.clone()); - tokio::spawn(async move { - loop { - let hannes_event = hannes.next_swarm_event().await; - println!("Hannes: {hannes_event:?}"); - } - }); - - tokio::spawn(async move { + let handler = tokio::spawn(async move { loop { - let bob_event = bob.next_swarm_event().await; - println!("Bob: {bob_event:?}"); + hannes.next_swarm_event().await; } }); - loop { - let alice_event = alice.next_swarm_event().await; - println!("Alice: {alice_event:?}"); + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { .. }], + [SwarmEvent::ExternalAddrExpired { .. }], + ) => {} + e => panic!("unknown events {e:#?}"), } + handler.abort(); } async fn new_server() -> Swarm { From faa694abde2031d7efefdc19d9ae25f8b51c2e64 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:58:18 +0100 Subject: [PATCH 14/97] Tests are working --- protocols/autonatv2/src/client.rs | 3 +- protocols/autonatv2/src/client/behaviour.rs | 17 +- .../src/client/handler/dial_request.rs | 47 +++- protocols/autonatv2/src/lib.rs | 15 -- protocols/autonatv2/src/server/behaviour.rs | 50 ++-- protocols/autonatv2/tests/autonatv2.rs | 232 ++++++++++++------ 6 files changed, 240 insertions(+), 124 deletions(-) diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index be1f6fd8df0..0e26a51499e 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,4 +1,5 @@ mod behaviour; mod handler; -pub use behaviour::Behaviour; +pub use behaviour::{Behaviour, Report}; +pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 70ecb4b3284..756e7f6815e 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -17,7 +17,11 @@ use rand_core::{OsRng, RngCore}; use crate::{global_only::IpExt, request_response::DialRequest}; -use super::handler::{dial_back, dial_request, Handler, TestEnd}; +use super::handler::{ + dial_back, + dial_request::{self, StatusUpdate}, + Handler, TestEnd, +}; struct IntervalTicker { interval: Duration, @@ -49,6 +53,12 @@ impl Default for Config { } } +#[derive(Debug)] +pub struct Report { + pub update: StatusUpdate, + pub peer_id: PeerId, +} + pub struct Behaviour where R: RngCore + 'static, @@ -76,7 +86,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = (); + type ToSwarm = Report; fn handle_established_inbound_connection( &mut self, @@ -209,6 +219,9 @@ where Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { tracing::debug!("Test failed: {:?}", err); } + Either::Left(dial_request::ToBehaviour::StatusUpdate(update)) => self + .pending_events + .push_back(ToSwarm::GenerateEvent(Report { update, peer_id })), } } diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index a4700f679d7..439d0148ffd 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,4 +1,7 @@ -use futures::{channel::oneshot, AsyncRead, AsyncWrite, AsyncWriteExt}; +use futures::{ + channel::{mpsc, oneshot}, + AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, +}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -58,7 +61,7 @@ pub enum Error { #[error("server experienced failure during dial back on address: {addr:?}")] FailureDuringDialBack { addr: Option }, #[error("error during substream upgrad")] - SubstreamError( + Substream( #[from] StreamUpgradeError< as OutboundUpgradeSend>::Error>, ), } @@ -73,9 +76,16 @@ pub struct TestEnd { #[derive(Debug)] pub enum ToBehaviour { TestCompleted(Result), + StatusUpdate(StatusUpdate), PeerHasServerSupport, } +#[derive(Debug)] +pub enum StatusUpdate { + GotDialDataReq { addr: Multiaddr, num_bytes: usize }, + CompletedDialData { addr: Multiaddr, num_bytes: usize }, +} + #[derive(Debug)] pub enum FromBehaviour { PerformRequest(DialRequest), @@ -98,14 +108,19 @@ pub struct Handler { >, >, >, + status_update_rx: mpsc::Receiver, + status_update_tx: mpsc::Sender, } impl Handler { pub(crate) fn new() -> Self { + let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), queued_streams: VecDeque::default(), + status_update_tx, + status_update_rx, } } @@ -118,7 +133,11 @@ impl Handler { }); if self .outbound - .try_push(start_substream_handle(req, rx)) + .try_push(start_substream_handle( + req, + rx, + self.status_update_tx.clone(), + )) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -157,6 +176,11 @@ impl ConnectionHandler for Handler { ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), )); } + if let Poll::Ready(Some(status_update)) = self.status_update_rx.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::StatusUpdate(status_update), + )); + } Poll::Pending } @@ -226,9 +250,10 @@ async fn start_substream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, + status_update_tx: mpsc::Sender, ) -> Result { match substream_recv.await { - Ok(Ok(substream)) => handle_substream(dial_request, substream).await, + Ok(Ok(substream)) => handle_substream(dial_request, substream, status_update_tx).await, Ok(Err(err)) => Err(Error::from(err)), Err(_) => Err(Error::InternalServer), } @@ -237,6 +262,7 @@ async fn start_substream_handle( async fn handle_substream( dial_request: DialRequest, substream: impl AsyncRead + AsyncWrite + Unpin, + mut status_update_tx: mpsc::Sender, ) -> Result { let mut coder = Coder::new(substream); coder @@ -267,10 +293,11 @@ async fn handle_substream( min: DATA_LEN_LOWER_BOUND, }); } - match dial_request.addrs.get(addr_idx) { + let addr = match dial_request.addrs.get(addr_idx).cloned() { Some(addr) => { tracing::trace!("the address {addr} is suspicious to the server, sending {num_bytes} bytes of data"); suspicious_addr.push(addr.clone()); + addr } None => { return Err(Error::InvalidReferencedAddress { @@ -278,9 +305,17 @@ async fn handle_substream( max: dial_request.addrs.len(), }); } - } + }; + let _ = status_update_tx + .send(StatusUpdate::GotDialDataReq { + addr: addr.clone(), + num_bytes, + }) + .await; + let status_update = StatusUpdate::CompletedDialData { addr, num_bytes }; send_aap_data(&mut coder, num_bytes).await?; + let _ = status_update_tx.send(status_update).await; } Response::Dial(dial_response) => { coder.close().await?; diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index 2b4db326485..00023a8b9c3 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -17,18 +17,3 @@ pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); pub type Nonce = u64; - -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 3f20fbf1bfb..c2592af17e2 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -139,10 +139,19 @@ where .. }) => { for (addr, _) in pairs.iter() { - if let Some((_, p)) = self.dialing_dial_back.keys().find(|(a, _)| a == addr) { - let cmds = self.dialing_dial_back.remove(&(addr.clone(), *p)).unwrap(); - for cmd in cmds { - let _ = cmd.back_channel.send(DialBack::Dial); + let cleaned_addr: Multiaddr = addr + .iter() + .filter(|p| !matches!(p, Protocol::P2p(_))) + .collect(); + let peer_id_opt = addr.iter().find_map(|p| match p { + Protocol::P2p(peer) => Some(peer), + _ => None, + }); + if let Some(peer_id) = peer_id_opt { + if let Some(cmd) = self.dialing_dial_back.remove(&(cleaned_addr, peer_id)) { + for cmd in cmd { + let _ = cmd.back_channel.send(DialBack::Dial); + } } } } @@ -167,27 +176,20 @@ where handler: NotifyHandler::One(*connection_id), event: Either::Left(cmd), }); + } else if let Some(pending) = + self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) + { + pending.push_back(cmd); } else { - if let Some(pending) = - self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) - { - pending.push_back(cmd); - } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr.clone()])) - .condition(PeerCondition::Always) - .allocate_new_port() - .build(), - /* opts: DialOpts::unknown_peer_id() - .address(addr.clone()) - .allocate_new_port() - .build(), - */ - }); - self.dialing_dial_back - .insert((addr, peer_id), VecDeque::from([cmd])); - } + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr.clone()])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(), + }); + self.dialing_dial_back + .insert((addr, peer_id), VecDeque::from([cmd])); } } Err(e) => { diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index 332918f249d..0e947ecdbdd 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -1,4 +1,7 @@ -use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmEvent}; +use futures::StreamExt; +use libp2p_autonatv2::client::{Report, StatusUpdate}; +use libp2p_core::{multiaddr::Protocol, Multiaddr}; +use libp2p_swarm::{DialError, NetworkBehaviour, Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt; use tracing_subscriber::EnvFilter; @@ -7,99 +10,111 @@ async fn confirm_successful() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); - let mut alice = new_server().await; - let cor_server_peer = alice.local_peer_id().clone(); - let mut bob = new_client().await; - let cor_client_peer = bob.local_peer_id().clone(); - bob.connect(&mut alice).await; + let (_alice, _bob) = bootstrap().await; +} - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { - peer_id: client_peer_sent, - })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( - libp2p_identify::Event::Received { - peer_id: client_peer_recv, - .. - }, - )), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { - peer_id: server_peer_sent, - })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( - libp2p_identify::Event::Received { - peer_id: server_peer_recv, - .. - }, - ))], - ) => { - assert_eq!(server_peer_sent, cor_server_peer); - assert_eq!(client_peer_sent, cor_client_peer); - assert_eq!(server_peer_recv, cor_server_peer); - assert_eq!(client_peer_recv, cor_client_peer); - } - e => panic!("unexpected events: {e:#?}"), - } +#[tokio::test] +async fn dial_back_to_unsupported_protocol() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let (mut alice, mut bob) = bootstrap().await; + + let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + + bob.behaviour_mut() + .autonat + .inject_test_addr(test_addr.clone()); match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( - [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], + [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(_), + .. + }], + [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::GotDialDataReq { + addr: addr_0, + num_bytes: req_num, + }, + .. + })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::CompletedDialData { + addr: addr_1, + num_bytes: done_num, + }, + .. + })), SwarmEvent::ExternalAddrExpired { .. }], ) => { - assert_eq!(addr_new, addr_snd); - assert_eq!(addr_snd, addr_ok); + assert_eq!(addr_0, test_addr); + assert_eq!(addr_1, test_addr); + assert_eq!(req_num, done_num); } e => panic!("unknown events {e:#?}"), } } #[tokio::test] -async fn dial_back_to_not_supporting() { +async fn dial_back_to_non_libp2p() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); - let mut alice = new_server().await; - let cor_server_peer = alice.local_peer_id().clone(); - let mut bob = new_client().await; - let cor_client_peer = bob.local_peer_id().clone(); - bob.connect(&mut alice).await; + let (mut alice, mut bob) = bootstrap().await; - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { - peer_id: client_peer_sent, - })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( - libp2p_identify::Event::Received { - peer_id: client_peer_recv, + for addr_str in [ + "/dns4/umgefahren.xyz/tcp/809", + "/ip4/169.150.247.38/tcp/32", + "/ip6/2400:52e0:1e02::1187:1/tcp/1000", + ] { + let addr: Multiaddr = addr_str.parse().unwrap(); + bob.behaviour_mut().autonat.inject_test_addr(addr.clone()); + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(mut peers), .. - }, - )), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { - peer_id: server_peer_sent, - })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( - libp2p_identify::Event::Received { - peer_id: server_peer_recv, + }], + [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::GotDialDataReq { + addr: addr_0, + num_bytes: req_num, + }, .. - }, - ))], - ) => { - assert_eq!(server_peer_sent, cor_server_peer); - assert_eq!(client_peer_sent, cor_client_peer); - assert_eq!(server_peer_recv, cor_server_peer); - assert_eq!(client_peer_recv, cor_client_peer); + })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::CompletedDialData { + addr: addr_1, + num_bytes: done_num, + }, + .. + })), SwarmEvent::ExternalAddrExpired { .. }], + ) => { + let (peer_addr, _) = peers.pop().unwrap(); + let cleaned_addr: Multiaddr = peer_addr + .iter() + .filter(|p| !matches!(p, Protocol::P2p(_))) + .collect(); + assert_eq!(addr, cleaned_addr); + assert_eq!(addr_0, cleaned_addr); + assert_eq!(addr_1, cleaned_addr); + assert_eq!(req_num, done_num); + } + e => panic!("unknown events {e:#?}"), } - e => panic!("unexpected events: {e:#?}"), } +} - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], - ) => { - assert_eq!(addr_new, addr_snd); - assert_eq!(addr_snd, addr_ok); - } - e => panic!("unknown events {e:#?}"), - } +#[tokio::test] +async fn dial_back_to_not_supporting() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let (mut alice, mut bob) = bootstrap().await; let mut hannes = new_dummy().await; let unreachable_address = hannes.external_addresses().next().unwrap().clone(); @@ -116,8 +131,26 @@ async fn dial_back_to_not_supporting() { match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { .. }], - [SwarmEvent::ExternalAddrExpired { .. }], - ) => {} + [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::GotDialDataReq { + addr: addr_0, + num_bytes: req_num, + }, + .. + })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::CompletedDialData { + addr: addr_1, + num_bytes: done_num, + }, + .. + })), SwarmEvent::ExternalAddrExpired { .. }], + ) => { + assert_eq!(addr_0, unreachable_address); + assert_eq!(addr_1, unreachable_address); + assert_eq!(req_num, done_num); + } e => panic!("unknown events {e:#?}"), } handler.abort(); @@ -172,3 +205,50 @@ async fn new_dummy() -> Swarm { node.listen().with_tcp_addr_external().await; node } + +async fn bootstrap() -> (Swarm, Swarm) { + let mut alice = new_server().await; + let cor_server_peer = alice.local_peer_id().clone(); + let mut bob = new_client().await; + let cor_client_peer = bob.local_peer_id().clone(); + bob.connect(&mut alice).await; + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + peer_id: client_peer_sent, + })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( + libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + }, + )), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + peer_id: server_peer_sent, + })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( + libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + }, + ))], + ) => { + assert_eq!(server_peer_sent, cor_server_peer); + assert_eq!(client_peer_sent, cor_client_peer); + assert_eq!(server_peer_recv, cor_server_peer); + assert_eq!(client_peer_recv, cor_client_peer); + } + e => panic!("unexpected events: {e:#?}"), + } + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], + ) => { + assert_eq!(addr_new, addr_snd); + assert_eq!(addr_snd, addr_ok); + } + e => panic!("unknown events {e:#?}"), + } + (alice, bob) +} From 6282cae0e81235a1610aa37cb078aea7bf651f46 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:42:18 +0100 Subject: [PATCH 15/97] Implement in call suggested changes --- Cargo.lock | 1 + core/src/transport/global_only.rs | 2 +- protocols/autonatv2/Cargo.toml | 3 +- protocols/autonatv2/src/client.rs | 2 +- protocols/autonatv2/src/client/behaviour.rs | 101 ++-- protocols/autonatv2/src/client/handler.rs | 49 +- .../src/client/handler/dial_request.rs | 196 ++++--- protocols/autonatv2/src/global_only.rs | 2 +- protocols/autonatv2/src/server.rs | 1 + protocols/autonatv2/src/server/behaviour.rs | 158 ++---- .../autonatv2/src/server/handler/dial_back.rs | 42 +- .../src/server/handler/dial_request.rs | 218 ++++--- protocols/autonatv2/tests/autonatv2.rs | 532 +++++++++++++----- swarm/src/lib.rs | 31 +- transports/tcp/src/lib.rs | 22 +- 15 files changed, 847 insertions(+), 513 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d17406cbae8..9ca6c813c3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2659,6 +2659,7 @@ dependencies = [ "either", "futures", "futures-bounded", + "futures-timer", "libp2p-core", "libp2p-identify", "libp2p-identity", diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 2963cc955d0..a40f99054d0 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -249,7 +249,7 @@ mod ip { // AS112-v6 (`2001:4:112::/48`) || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) )) || Ipv6Ext::is_documentation(self) || Ipv6Ext::is_unique_local(self) diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 3f5ccf0b68e..55b778c6ef9 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -25,9 +25,10 @@ bytes = "1" static_assertions = "1.1.0" tracing = "0.1.40" unsigned-varint = { workspace = true, features = ["futures"] } +futures-timer = "3.0.2" [dev-dependencies] -tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] } libp2p-swarm-test = { workspace = true } libp2p-identify = { workspace = true } libp2p-swarm = { workspace = true, features = ["macros"] } diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index 0e26a51499e..fc15af146f3 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,5 +1,5 @@ mod behaviour; mod handler; -pub use behaviour::{Behaviour, Report}; +pub use behaviour::{Behaviour, Config}; pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 756e7f6815e..47f6ece4d07 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,10 +1,12 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, task::{Context, Poll}, - time::{Duration, Instant}, + time::Duration, }; use either::Either; +use futures::FutureExt; +use futures_timer::Delay; use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -23,25 +25,34 @@ use super::handler::{ Handler, TestEnd, }; -struct IntervalTicker { - interval: Duration, - last_tick: Instant, +#[derive(Debug, Clone, Copy)] +pub struct Config { + pub(crate) test_server_count: usize, + pub(crate) max_addrs_count: usize, + pub(crate) recheck_interval: Duration, } -impl IntervalTicker { - fn ready(&mut self) -> bool { - if self.last_tick.elapsed() >= self.interval { - self.last_tick = Instant::now(); - true - } else { - false +impl Config { + pub fn with_test_server_count(self, test_server_count: usize) -> Self { + Self { + test_server_count, + ..self } } -} -pub struct Config { - pub(crate) test_server_count: usize, - pub(crate) max_addrs_count: usize, + pub fn with_max_addrs_count(self, max_addrs_count: usize) -> Self { + Self { + max_addrs_count, + ..self + } + } + + pub fn with_recheck_interval(self, recheck_interval: Duration) -> Self { + Self { + recheck_interval, + ..self + } + } } impl Default for Config { @@ -49,16 +60,10 @@ impl Default for Config { Self { test_server_count: 3, max_addrs_count: 10, + recheck_interval: Duration::from_secs(5), } } } - -#[derive(Debug)] -pub struct Report { - pub update: StatusUpdate, - pub peer_id: PeerId, -} - pub struct Behaviour where R: RngCore + 'static, @@ -77,7 +82,7 @@ where address_candidates: HashMap, already_tested: HashSet, peers_to_handlers: HashMap, - ticker: IntervalTicker, + next_tick: Delay, } impl NetworkBehaviour for Behaviour @@ -86,7 +91,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = Report; + type ToSwarm = StatusUpdate; fn handle_established_inbound_connection( &mut self, @@ -104,7 +109,7 @@ where fn handle_established_outbound_connection( &mut self, connection_id: ConnectionId, - _peer: PeerId, + peer: PeerId, addr: &Multiaddr, _role_override: Endpoint, _port_use: PortUse, @@ -112,7 +117,7 @@ where if addr_is_local(addr) { self.local_peers.insert(connection_id); } - Ok(Either::Left(dial_request::Handler::new())) + Ok(Either::Left(dial_request::Handler::new(peer))) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -183,7 +188,6 @@ where } Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { dial_request: DialRequest { nonce, addrs }, - suspicious_addr, reachable_addr, }))) => { if self.pending_nonces.remove(&nonce) { @@ -192,12 +196,6 @@ where ); return; } - if !suspicious_addr.is_empty() { - tracing::trace!( - "server reported suspicious addresses: {:?}", - suspicious_addr - ); - } self.pending_events.extend( addrs .into_iter() @@ -207,33 +205,35 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } - Either::Left(dial_request::ToBehaviour::TestCompleted(Err( - dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }, - ))) - | Either::Left(dial_request::ToBehaviour::TestCompleted(Err( - dial_request::Error::FailureDuringDialBack { addr: Some(addr) }, - ))) => { - self.pending_events - .push_back(ToSwarm::ExternalAddrExpired(addr)); - } Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { - tracing::debug!("Test failed: {:?}", err); + match err.internal.as_ref() { + dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } + | dial_request::InternalError::UnableToConnectOnSelectedAddress { + addr: Some(addr), + } => { + self.pending_events + .push_back(ToSwarm::ExternalAddrExpired(addr.clone())); + } + _ => { + tracing::debug!("Test failed: {:?}", err); + } + } } Either::Left(dial_request::ToBehaviour::StatusUpdate(update)) => self .pending_events - .push_back(ToSwarm::GenerateEvent(Report { update, peer_id })), + .push_back(ToSwarm::GenerateEvent(update)), } } fn poll( &mut self, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { let pending_event = self.poll_pending_events(); if pending_event.is_ready() { return pending_event; } - if self.ticker.ready() + if self.next_tick.poll_unpin(cx).is_ready() && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { @@ -282,15 +282,12 @@ where pending_nonces: HashSet::new(), known_servers: Vec::new(), rng, + next_tick: Delay::new(config.recheck_interval), config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), peers_to_handlers: HashMap::new(), already_tested: HashSet::new(), - ticker: IntervalTicker { - interval: Duration::from_secs(0), - last_tick: Instant::now(), - }, } } @@ -327,10 +324,6 @@ where } Poll::Pending } - - pub fn inject_test_addr(&mut self, addr: Multiaddr) { - *self.address_candidates.entry(addr).or_default() += 1; - } } impl Default for Behaviour { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index e4a48992727..0776e45e5bd 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,20 +1,51 @@ -// two handlers, share state in behaviour -// do isolated stuff in async function -// -// write basic tests -// Take a look at rendezvous -// TODO: tests -// TODO: Handlers - pub(crate) mod dial_back; pub(crate) mod dial_request; use either::Either; -use std::time::Duration; +use std::{ + fmt::{Display, Formatter}, + sync::Arc, + time::Duration, +}; pub(crate) use dial_request::TestEnd; +use self::dial_request::InternalError; + const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; pub(crate) type Handler = Either; + +#[derive(Clone, Debug)] +pub struct Error { + pub(crate) internal: Arc, +} + +impl From for Error { + fn from(value: InternalError) -> Self { + Self { + internal: Arc::new(value), + } + } +} + +impl From> for Error { + fn from(value: Arc) -> Self { + Self { internal: value } + } +} + +impl From<&Arc> for Error { + fn from(value: &Arc) -> Self { + Self { + internal: Arc::clone(value), + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.internal.fmt(f) + } +} diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index 439d0148ffd..d3a13218ace 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,6 +1,6 @@ use futures::{ channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, + AsyncRead, AsyncWrite, SinkExt, StreamExt, }; use futures_bounded::FuturesSet; use libp2p_core::{ @@ -8,6 +8,7 @@ use libp2p_core::{ Multiaddr, }; +use libp2p_identity::PeerId; use libp2p_swarm::{ handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, @@ -21,6 +22,7 @@ use std::{ convert::identity, io, iter::{once, repeat}, + sync::Arc, task::{Context, Poll}, }; @@ -37,7 +39,7 @@ use crate::{ use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; #[derive(Debug, thiserror::Error)] -pub enum Error { +pub(crate) enum InternalError { #[error("io error")] Io(#[from] io::Error), #[error("invalid referenced address index: {index} (max number of addr: {max})")] @@ -69,21 +71,22 @@ pub enum Error { #[derive(Debug)] pub struct TestEnd { pub(crate) dial_request: DialRequest, - pub(crate) suspicious_addr: Vec, pub(crate) reachable_addr: Multiaddr, } #[derive(Debug)] pub enum ToBehaviour { - TestCompleted(Result), + TestCompleted(Result), StatusUpdate(StatusUpdate), PeerHasServerSupport, } #[derive(Debug)] -pub enum StatusUpdate { - GotDialDataReq { addr: Multiaddr, num_bytes: usize }, - CompletedDialData { addr: Multiaddr, num_bytes: usize }, +pub struct StatusUpdate { + pub tested_addr: Option, + pub data_amount: usize, + pub server: PeerId, + pub result: Result<(), crate::client::handler::Error>, } #[derive(Debug)] @@ -99,7 +102,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet>, queued_streams: VecDeque< oneshot::Sender< Result< @@ -110,10 +113,11 @@ pub struct Handler { >, status_update_rx: mpsc::Receiver, status_update_tx: mpsc::Sender, + server: PeerId, } impl Handler { - pub(crate) fn new() -> Self { + pub(crate) fn new(server: PeerId) -> Self { let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), @@ -121,6 +125,7 @@ impl Handler { queued_streams: VecDeque::default(), status_update_tx, status_update_rx, + server, } } @@ -134,6 +139,7 @@ impl Handler { if self .outbound .try_push(start_substream_handle( + self.server, req, rx, self.status_update_tx.clone(), @@ -173,7 +179,12 @@ impl ConnectionHandler for Handler { } if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), + ToBehaviour::TestCompleted( + m.map_err(InternalError::Timeout) + .map_err(Into::into) + .and_then(identity) + .map_err(Into::into), + ), )); } if let Poll::Ready(Some(status_update)) = self.status_update_rx.poll_next_unpin(cx) { @@ -206,7 +217,7 @@ impl ConnectionHandler for Handler { tracing::debug!("Dial request failed: {}", error); match self.queued_streams.pop_front() { Some(stream_tx) => { - if let Err(_) = stream_tx.send(Err(error)) { + if stream_tx.send(Err(error)).is_err() { tracing::warn!("Failed to send stream to dead handler"); } } @@ -221,7 +232,7 @@ impl ConnectionHandler for Handler { protocol, .. }) => match self.queued_streams.pop_front() { Some(stream_tx) => { - if let Err(_) = stream_tx.send(Ok(protocol)) { + if stream_tx.send(Ok(protocol)).is_err() { tracing::warn!("Failed to send stream to dead handler"); } } @@ -243,6 +254,7 @@ impl ConnectionHandler for Handler { } async fn start_substream_handle( + server: PeerId, dial_request: DialRequest, substream_recv: oneshot::Receiver< Result< @@ -250,133 +262,143 @@ async fn start_substream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, - status_update_tx: mpsc::Sender, -) -> Result { - match substream_recv.await { - Ok(Ok(substream)) => handle_substream(dial_request, substream, status_update_tx).await, - Ok(Err(err)) => Err(Error::from(err)), - Err(_) => Err(Error::InternalServer), - } + mut status_update_tx: mpsc::Sender, +) -> Result { + let substream = match substream_recv.await { + Ok(Ok(substream)) => substream, + Ok(Err(err)) => return Err(InternalError::from(err).into()), + Err(_) => return Err(InternalError::InternalServer.into()), + }; + let mut data_amount = 0; + let mut checked_addr_idx = None; + let addrs = dial_request.addrs.clone(); + let res = handle_substream( + dial_request, + substream, + &mut data_amount, + &mut checked_addr_idx, + ) + .await + .map_err(Arc::new) + .map_err(crate::client::handler::Error::from); + let status_update = StatusUpdate { + tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), + data_amount, + server, + result: res.as_ref().map(|_| ()).map_err(|e| e.clone()), + }; + let _ = status_update_tx.send(status_update).await; + res } async fn handle_substream( dial_request: DialRequest, substream: impl AsyncRead + AsyncWrite + Unpin, - mut status_update_tx: mpsc::Sender, -) -> Result { + data_amount: &mut usize, + checked_addr_idx: &mut Option, +) -> Result { let mut coder = Coder::new(substream); coder .send_request(Request::Dial(dial_request.clone())) .await?; - let mut suspicious_addr = Vec::new(); - loop { - match coder.next_response().await? { - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => { - if addr_idx >= dial_request.addrs.len() { - return Err(Error::InvalidReferencedAddress { - index: addr_idx, - max: dial_request.addrs.len(), - }); - } - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(Error::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); - } - if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(Error::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - let addr = match dial_request.addrs.get(addr_idx).cloned() { - Some(addr) => { - tracing::trace!("the address {addr} is suspicious to the server, sending {num_bytes} bytes of data"); - suspicious_addr.push(addr.clone()); - addr - } - None => { - return Err(Error::InvalidReferencedAddress { - index: addr_idx, - max: dial_request.addrs.len(), - }); - } - }; - - let _ = status_update_tx - .send(StatusUpdate::GotDialDataReq { - addr: addr.clone(), - num_bytes, - }) - .await; - let status_update = StatusUpdate::CompletedDialData { addr, num_bytes }; - send_aap_data(&mut coder, num_bytes).await?; - let _ = status_update_tx.send(status_update).await; + match coder.next_response().await? { + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if addr_idx >= dial_request.addrs.len() { + return Err(InternalError::InvalidReferencedAddress { + index: addr_idx, + max: dial_request.addrs.len(), + }); + } + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(InternalError::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } + if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(InternalError::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); } - Response::Dial(dial_response) => { + *checked_addr_idx = Some(addr_idx); + send_aap_data(&mut coder, num_bytes, data_amount).await?; + if let Response::Dial(dial_response) = coder.next_response().await? { + *checked_addr_idx = Some(dial_response.addr_idx); coder.close().await?; - return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); + test_end_from_dial_response(dial_request, dial_response) + } else { + Err(InternalError::InternalServer) } } + Response::Dial(dial_response) => { + *checked_addr_idx = Some(dial_response.addr_idx); + coder.close().await?; + test_end_from_dial_response(dial_request, dial_response) + } } } fn test_end_from_dial_response( req: DialRequest, resp: DialResponse, - suspicious_addr: Vec, -) -> Result { +) -> Result { if resp.addr_idx >= req.addrs.len() { - return Err(Error::InvalidReferencedAddress { + return Err(InternalError::InvalidReferencedAddress { index: resp.addr_idx, max: req.addrs.len(), }); } match (resp.status, resp.dial_status) { - (ResponseStatus::E_REQUEST_REJECTED, _) => Err(Error::ServerRejectedDialRequest), - (ResponseStatus::E_DIAL_REFUSED, _) => Err(Error::ServerChoseNotToDialAnyAddress), - (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServer), - (ResponseStatus::OK, DialStatus::UNUSED) => Err(Error::InvalidResponse), + (ResponseStatus::E_REQUEST_REJECTED, _) => Err(InternalError::ServerRejectedDialRequest), + (ResponseStatus::E_DIAL_REFUSED, _) => Err(InternalError::ServerChoseNotToDialAnyAddress), + (ResponseStatus::E_INTERNAL_ERROR, _) => Err(InternalError::InternalServer), + (ResponseStatus::OK, DialStatus::UNUSED) => Err(InternalError::InvalidResponse), (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { - Err(Error::UnableToConnectOnSelectedAddress { + Err(InternalError::UnableToConnectOnSelectedAddress { + addr: req.addrs.get(resp.addr_idx).cloned(), + }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { + Err(InternalError::FailureDuringDialBack { addr: req.addrs.get(resp.addr_idx).cloned(), }) } - (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => Err(Error::FailureDuringDialBack { - addr: req.addrs.get(resp.addr_idx).cloned(), - }), (ResponseStatus::OK, DialStatus::OK) => req .addrs .get(resp.addr_idx) - .ok_or(Error::InvalidReferencedAddress { + .ok_or(InternalError::InvalidReferencedAddress { index: resp.addr_idx, max: req.addrs.len(), }) .cloned() .map(|reachable_addr| TestEnd { dial_request: req, - suspicious_addr, reachable_addr, }), } } -async fn send_aap_data(substream: &mut Coder, num_bytes: usize) -> io::Result<()> +async fn send_aap_data( + substream: &mut Coder, + num_bytes: usize, + data_amount: &mut usize, +) -> io::Result<()> where I: AsyncWrite + Unpin, { let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; - for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) + for (data_count, req) in repeat(DATA_FIELD_LEN_UPPER_BOUND) .take(count_full) .chain(once(partial_len)) .filter(|e| *e > 0) - .map(|data_count| Request::Data(DialDataResponse { data_count })) + .map(|data_count| (data_count, Request::Data(DialDataResponse { data_count }))) { + *data_amount += data_count; substream.send_request(req).await?; } Ok(()) diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonatv2/src/global_only.rs index e549030eff9..af788639399 100644 --- a/protocols/autonatv2/src/global_only.rs +++ b/protocols/autonatv2/src/global_only.rs @@ -228,7 +228,7 @@ impl IpExt for Ipv6Addr { // AS112-v6 (`2001:4:112::/48`) || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) )) || Ipv6Ext::is_documentation(self) || Ipv6Ext::is_unique_local(self) diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs index be1f6fd8df0..ef746607559 100644 --- a/protocols/autonatv2/src/server.rs +++ b/protocols/autonatv2/src/server.rs @@ -2,3 +2,4 @@ mod behaviour; mod handler; pub use behaviour::Behaviour; +pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index c2592af17e2..9480a113571 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -1,19 +1,17 @@ -use std::fmt::Debug; use std::{ collections::{HashMap, VecDeque}, task::{Context, Poll}, }; -use crate::server::handler::dial_request::DialBack; +use crate::server::handler::dial_request::DialBackStatus; use either::Either; -use libp2p_core::multiaddr::Protocol; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; -use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ - dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialError, DialFailure, - FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, + dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, + NetworkBehaviour, ToSwarm, }; +use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; use super::handler::{ @@ -26,8 +24,7 @@ pub struct Behaviour where R: Clone + Send + RngCore + 'static, { - handlers: HashMap<(Multiaddr, PeerId), ConnectionId>, - dialing_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, + dialing_dial_back: HashMap, pending_events: VecDeque< ToSwarm< ::ToSwarm, @@ -49,7 +46,6 @@ where { pub fn new(rng: R) -> Self { Self { - handlers: HashMap::new(), dialing_dial_back: HashMap::new(), pending_events: VecDeque::new(), rng, @@ -78,16 +74,17 @@ where { type ConnectionHandler = Handler; - type ToSwarm = (); + type ToSwarm = crate::server::handler::dial_request::StatusUpdate; fn handle_established_inbound_connection( &mut self, _connection_id: ConnectionId, - _peer: PeerId, + peer: PeerId, _local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result<::ConnectionHandler, ConnectionDenied> { Ok(Either::Right(dial_request::Handler::new( + peer, remote_addr.clone(), self.rng.clone(), ))) @@ -96,64 +93,28 @@ where fn handle_established_outbound_connection( &mut self, connection_id: ConnectionId, - peer: PeerId, - addr: &Multiaddr, + _peer: PeerId, + _addr: &Multiaddr, _role_override: Endpoint, - port_use: PortUse, + _port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - if port_use == PortUse::New { - self.handlers.insert( - ( - addr.iter() - .filter(|e| !matches!(e, Protocol::P2p(_))) - .collect(), - peer, - ), - connection_id, - ); - } - Ok(Either::Left(dial_back::Handler::new())) + Ok( + if let Some(cmd) = self.dialing_dial_back.remove(&connection_id) { + Either::Left(dial_back::Handler::new(cmd)) + } else { + Either::Left(dial_back::Handler::empty()) + }, + ) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { - FromSwarm::DialFailure(DialFailure { - error: DialError::WrongPeerId { .. }, - peer_id: Some(peer_id), - .. - }) => { - if let Some(key) = self - .dialing_dial_back - .keys() - .find(|(_, p)| *p == peer_id) - .cloned() + FromSwarm::DialFailure(DialFailure { connection_id, .. }) + | FromSwarm::ConnectionClosed(ConnectionClosed { connection_id, .. }) => { + if let Some(DialBackCommand { back_channel, .. }) = + self.dialing_dial_back.remove(&connection_id) { - let cmds = self.dialing_dial_back.remove(&key).unwrap(); - for cmd in cmds { - let _ = cmd.back_channel.send(DialBack::Dial); - } - } - } - FromSwarm::DialFailure(DialFailure { - error: DialError::Transport(pairs), - .. - }) => { - for (addr, _) in pairs.iter() { - let cleaned_addr: Multiaddr = addr - .iter() - .filter(|p| !matches!(p, Protocol::P2p(_))) - .collect(); - let peer_id_opt = addr.iter().find_map(|p| match p { - Protocol::P2p(peer) => Some(peer), - _ => None, - }); - if let Some(peer_id) = peer_id_opt { - if let Some(cmd) = self.dialing_dial_back.remove(&(cleaned_addr, peer_id)) { - for cmd in cmd { - let _ = cmd.back_channel.send(DialBack::Dial); - } - } - } + let _ = back_channel.send(DialBackStatus::DialErr); } } _ => {} @@ -166,36 +127,28 @@ where _connection_id: ConnectionId, event: as ConnectionHandler>::ToBehaviour, ) { - if let Either::Right(m) = event { - match m { - Ok(cmd) => { - let addr = cmd.addr.clone(); - if let Some(connection_id) = self.handlers.get(&(addr.clone(), peer_id)) { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(*connection_id), - event: Either::Left(cmd), - }); - } else if let Some(pending) = - self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) - { - pending.push_back(cmd); - } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr.clone()])) - .condition(PeerCondition::Always) - .allocate_new_port() - .build(), - }); - self.dialing_dial_back - .insert((addr, peer_id), VecDeque::from([cmd])); - } - } - Err(e) => { - tracing::warn!("incoming dial request failed: {}", e); - } + match event { + Either::Left(Ok(_)) => {} + Either::Left(Err(e)) => { + tracing::debug!("dial back error: {e:?}"); + } + Either::Right(Either::Left(Ok(cmd))) => { + let addr = cmd.addr.clone(); + let opts = DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(); + let conn_id = opts.connection_id(); + self.dialing_dial_back.insert(conn_id, cmd); + self.pending_events.push_back(ToSwarm::Dial { opts }); + } + Either::Right(Either::Left(Err(e))) => { + tracing::warn!("incoming dial request failed: {}", e); } + Either::Right(Either::Right(status_update)) => self + .pending_events + .push_back(ToSwarm::GenerateEvent(status_update)), } } @@ -207,29 +160,6 @@ where if pending_event.is_ready() { return pending_event; } - if let Some((addr, peer)) = self - .dialing_dial_back - .keys() - .filter(|k| self.handlers.contains_key(*k)) - .next() - .cloned() - { - let cmds = self - .dialing_dial_back - .remove(&(addr.clone(), peer)) - .unwrap(); - let cmd_n = cmds.len(); - for cmd in cmds { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: peer.clone(), - handler: NotifyHandler::One(self.handlers[&(addr.clone(), peer)]), - event: Either::Left(cmd), - }); - } - if cmd_n > 0 { - return self.poll_pending_events(cx); - } - } Poll::Pending } } diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index c282ff522dc..8a497dc1f84 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -1,5 +1,4 @@ use std::{ - collections::VecDeque, convert::identity, io, task::{Context, Poll}, @@ -10,36 +9,41 @@ use futures::{AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, SubstreamProtocol, }; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME, DIAL_BACK_UPGRADE}; +use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; -use super::dial_request::{DialBack as DialBackRes, DialBackCommand}; +use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; pub(crate) type ToBehaviour = io::Result<()>; -pub(crate) type FromBehaviour = DialBackCommand; pub struct Handler { - pending_nonce: VecDeque, - requested_substream_nonce: VecDeque, + pending_nonce: Option, + requested_substream_nonce: Option, outbound: FuturesSet, } impl Handler { - pub(crate) fn new() -> Self { + pub(crate) fn new(cmd: DialBackCommand) -> Self { + let mut ret = Self::empty(); + ret.pending_nonce = Some(cmd); + ret + } + + pub(crate) fn empty() -> Self { Self { - pending_nonce: VecDeque::new(), - requested_substream_nonce: VecDeque::new(), + pending_nonce: None, + requested_substream_nonce: None, outbound: FuturesSet::new(Duration::from_secs(10000), 2), } } } impl ConnectionHandler for Handler { - type FromBehaviour = FromBehaviour; + type FromBehaviour = (); type ToBehaviour = ToBehaviour; type InboundProtocol = DeniedUpgrade; type OutboundProtocol = ReadyUpgrade; @@ -63,8 +67,8 @@ impl ConnectionHandler for Handler { .and_then(identity), )); } - if let Some(cmd) = self.pending_nonce.pop_front() { - self.requested_substream_nonce.push_back(cmd); + if let Some(cmd) = self.pending_nonce.take() { + self.requested_substream_nonce = Some(cmd); return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(DIAL_BACK_UPGRADE, ()), }); @@ -72,9 +76,7 @@ impl ConnectionHandler for Handler { Poll::Pending } - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - self.pending_nonce.push_back(event); - } + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} fn on_connection_event( &mut self, @@ -89,7 +91,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { protocol, .. }) => { - if let Some(cmd) = self.requested_substream_nonce.pop_front() { + if let Some(cmd) = self.requested_substream_nonce.take() { if self .outbound .try_push(perform_dial_back(protocol, cmd)) @@ -109,8 +111,8 @@ impl ConnectionHandler for Handler { error: StreamUpgradeError::Timeout, .. }) => { - if let Some(cmd) = self.requested_substream_nonce.pop_front() { - let _ = cmd.back_channel.send(DialBackRes::DialBack); + if let Some(cmd) = self.requested_substream_nonce.take() { + let _ = cmd.back_channel.send(DialBackRes::DialBackErr); } } _ => {} @@ -128,7 +130,7 @@ async fn perform_dial_back( ) -> io::Result<()> { let res = perform_dial_back_inner(&mut stream, nonce) .await - .map_err(|_| DialBackRes::DialBack) + .map_err(|_| DialBackRes::DialBackErr) .map(|_| DialBackRes::Ok) .unwrap_or_else(|e| e); back_channel diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 6a7cf89de0c..db1e6b284e6 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -1,19 +1,21 @@ use std::{ io, + sync::Arc, task::{Context, Poll}, time::Duration, }; -use futures::future::FusedFuture; +use either::Either; use futures::{ channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, + AsyncRead, AsyncWrite, SinkExt, StreamExt, }; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, }; +use libp2p_identity::PeerId; use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, @@ -29,11 +31,21 @@ use crate::{ Nonce, REQUEST_UPGRADE, }; +#[derive(Clone, Debug)] +pub struct StatusUpdate { + pub all_addrs: Vec, + pub tested_addr: Option, + pub client: PeerId, + pub data_amount: usize, + pub result: Result<(), Arc>, +} + #[derive(Debug, PartialEq)] -pub(crate) enum DialBack { - #[allow(unused)] - Dial, - DialBack, +pub(crate) enum DialBackStatus { + /// Failure during dial + DialErr, + /// Failure during dial back + DialBackErr, Ok, } @@ -41,14 +53,17 @@ pub(crate) enum DialBack { pub struct DialBackCommand { pub(crate) addr: Multiaddr, pub(crate) nonce: Nonce, - pub(crate) back_channel: oneshot::Sender, + pub(crate) back_channel: oneshot::Sender, } pub struct Handler { + client_id: PeerId, observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, dial_back_cmd_receiver: mpsc::Receiver, - inbound: FuturesSet>, + status_update_sender: mpsc::Sender, + status_update_receiver: mpsc::Receiver, + inbound: FuturesSet>>, rng: R, } @@ -56,12 +71,16 @@ impl Handler where R: RngCore, { - pub(crate) fn new(observed_multiaddr: Multiaddr, rng: R) -> Self { + pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); + let (status_update_sender, status_update_receiver) = mpsc::channel(10); Self { + client_id, observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, + status_update_sender, + status_update_receiver, inbound: FuturesSet::new(Duration::from_secs(10), 10), rng, } @@ -74,7 +93,7 @@ where { type FromBehaviour = (); - type ToBehaviour = io::Result; + type ToBehaviour = Either>, StatusUpdate>; type InboundProtocol = ReadyUpgrade; @@ -96,18 +115,27 @@ where > { match self.inbound.poll_unpin(cx) { Poll::Ready(Ok(Err(e))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err(e))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( + e, + )))); } Poll::Ready(Err(e)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err( - io::Error::new(io::ErrorKind::TimedOut, e), - ))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( + Arc::new(io::Error::new(io::ErrorKind::TimedOut, e)), + )))); } Poll::Ready(Ok(Ok(_))) => {} Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Ok( + cmd, + )))); + } + if let Poll::Ready(Some(status_update)) = self.status_update_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + status_update, + ))); } Poll::Pending } @@ -129,10 +157,12 @@ where }) => { if self .inbound - .try_push(handle_request( + .try_push(start_handle_request( protocol, self.observed_multiaddr.clone(), + self.client_id, self.dial_back_cmd_sender.clone(), + self.status_update_sender.clone(), self.rng.clone(), )) .is_err() @@ -158,7 +188,7 @@ enum HandleFail { InternalError(usize), RequestRejected, DialRefused, - DialBack { idx: usize, err: DialBack }, + DialBack { idx: usize, err: DialBackStatus }, } impl From for DialResponse { @@ -183,9 +213,9 @@ impl From for DialResponse { status: ResponseStatus::OK, addr_idx: idx, dial_status: match err { - DialBack::Dial => DialStatus::E_DIAL_ERROR, - DialBack::DialBack => DialStatus::E_DIAL_BACK_ERROR, - DialBack::Ok => DialStatus::OK, + DialBackStatus::DialErr => DialStatus::E_DIAL_ERROR, + DialBackStatus::DialBackErr => DialStatus::E_DIAL_BACK_ERROR, + DialBackStatus::Ok => DialStatus::OK, }, }, } @@ -197,11 +227,14 @@ async fn handle_request_internal( observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, mut rng: impl RngCore, + all_addrs: &mut Vec, + tested_addrs: &mut Option, + data_amount: &mut usize, ) -> Result where I: AsyncRead + AsyncWrite + Unpin, { - let DialRequest { addrs, nonce } = match coder + let DialRequest { mut addrs, nonce } = match coder .next_request() .await .map_err(|_| HandleFail::InternalError(0))? @@ -211,53 +244,61 @@ where return Err(HandleFail::RequestRejected); } }; - for (idx, addr) in addrs.into_iter().enumerate() { - if addr != observed_multiaddr { - let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); - let mut rem_data = dial_data_request.num_bytes; - coder - .send_response(Response::Data(dial_data_request)) - .await - .map_err(|_| HandleFail::InternalError(idx))?; - while rem_data > 0 { - let DialDataResponse { data_count } = match coder - .next_request() - .await - .map_err(|e| HandleFail::InternalError(idx))? - { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => dial_data_response, - }; - rem_data = rem_data.saturating_sub(data_count); - } - } - let (back_channel, rx) = oneshot::channel(); - let dial_back_cmd = DialBackCommand { - addr, - nonce, - back_channel, - }; - dial_back_cmd_sender - .clone() - .send(dial_back_cmd) + *all_addrs = addrs.clone(); + let idx = 0; + let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; + *tested_addrs = Some(addr.clone()); + *data_amount = 0; + if addr != observed_multiaddr { + let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); + let mut rem_data = dial_data_request.num_bytes; + coder + .send_response(Response::Data(dial_data_request)) .await .map_err(|_| HandleFail::InternalError(idx))?; - let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; - if dial_back != DialBack::Ok { - return Err(HandleFail::DialBack { - idx, - err: dial_back, - }); + while rem_data > 0 { + let DialDataResponse { data_count } = match coder + .next_request() + .await + .map_err(|_e| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response, + }; + rem_data = rem_data.saturating_sub(data_count); + *data_amount += data_count; } - return Ok(DialResponse { - status: ResponseStatus::OK, - addr_idx: idx, - dial_status: DialStatus::OK, + } + let (back_channel, rx) = oneshot::channel(); + let dial_back_cmd = DialBackCommand { + addr, + nonce, + back_channel, + }; + dial_back_cmd_sender + .clone() + .send(dial_back_cmd) + .await + .map_err(|_| HandleFail::DialBack { + idx, + err: DialBackStatus::DialErr, + })?; + + // TODO: add timeout + let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; + if dial_back != DialBackStatus::Ok { + return Err(HandleFail::DialBack { + idx, + err: dial_back, }); } - Err(HandleFail::DialRefused) + Ok(DialResponse { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: DialStatus::OK, + }) } async fn handle_request( @@ -265,13 +306,56 @@ async fn handle_request( observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, rng: impl RngCore, + all_addrs: &mut Vec, + tested_addrs: &mut Option, + data_amount: &mut usize, ) -> io::Result<()> { let mut coder = Coder::new(stream); - let response = - handle_request_internal(&mut coder, observed_multiaddr, dial_back_cmd_sender, rng) - .await - .unwrap_or_else(|e| e.into()); + let response = handle_request_internal( + &mut coder, + observed_multiaddr, + dial_back_cmd_sender, + rng, + all_addrs, + tested_addrs, + data_amount, + ) + .await + .unwrap_or_else(|e| e.into()); coder.send_response(Response::Dial(response)).await?; coder.close().await?; Ok(()) } + +async fn start_handle_request( + stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + client: PeerId, + dial_back_cmd_sender: mpsc::Sender, + mut status_update_sender: mpsc::Sender, + rng: impl RngCore, +) -> Result<(), Arc> { + let mut all_addrs = Vec::new(); + let mut tested_addrs = None; + let mut data_amount = 0; + let res = handle_request( + stream, + observed_multiaddr, + dial_back_cmd_sender, + rng, + &mut all_addrs, + &mut tested_addrs, + &mut data_amount, + ) + .await + .map_err(Arc::new); + let status_update = StatusUpdate { + all_addrs, + tested_addr: tested_addrs, + client, + data_amount, + result: res.as_ref().map_err(Arc::clone).map(|_| ()), + }; + let _ = status_update_sender.send(status_update).await; + res +} diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index 0e947ecdbdd..bc1c2b52c36 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -1,8 +1,15 @@ -use futures::StreamExt; -use libp2p_autonatv2::client::{Report, StatusUpdate}; -use libp2p_core::{multiaddr::Protocol, Multiaddr}; -use libp2p_swarm::{DialError, NetworkBehaviour, Swarm, SwarmEvent}; +use libp2p_autonatv2::client::{self, Config}; +use libp2p_autonatv2::server; +use libp2p_core::transport::TransportError; +use libp2p_core::Multiaddr; +use libp2p_swarm::{ + DialError, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, Swarm, SwarmEvent, +}; use libp2p_swarm_test::SwarmExt; +use rand_core::OsRng; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::oneshot; use tracing_subscriber::EnvFilter; #[tokio::test] @@ -20,40 +27,102 @@ async fn dial_back_to_unsupported_protocol() { .try_init(); let (mut alice, mut bob) = bootstrap().await; - let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + let alice_peer_id = *alice.local_peer_id(); + let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + let bob_test_addr = test_addr.clone(); bob.behaviour_mut() .autonat - .inject_test_addr(test_addr.clone()); + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &test_addr }, + )); - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(_), - .. - }], - [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::GotDialDataReq { - addr: addr_0, - num_bytes: req_num, - }, - .. - })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::CompletedDialData { - addr: addr_1, - num_bytes: done_num, - }, - .. - })), SwarmEvent::ExternalAddrExpired { .. }], - ) => { - assert_eq!(addr_0, test_addr); - assert_eq!(addr_1, test_addr); - assert_eq!(req_num, done_num); + let (bob_done_tx, bob_done_rx) = oneshot::channel(); + + let alice_task = async move { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|e| (e, connection_id)), + _ => None, + }) + .await; + let mut outgoing_conn_error = alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::Transport(transport_errs), + } if connection_id == alice_conn_id && alice_dialing_peer == peer_id => { + Some(transport_errs) + } + _ => None, + }) + .await; + if let Some((multiaddr, TransportError::MultiaddrNotSupported(not_supported_addr))) = + outgoing_conn_error.pop() + { + assert_eq!( + multiaddr, + test_addr.clone().with_p2p(alice_dialing_peer).unwrap() + ); + assert_eq!(not_supported_addr, multiaddr,); + } else { + panic!("Peers are empty"); } - e => panic!("unknown events {e:#?}"), - } + assert_eq!(outgoing_conn_error.len(), 0); + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + all_addrs, + tested_addr: Some(tested_addr), + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![test_addr.clone()] + && tested_addr == test_addr.clone() + && client == alice_dialing_peer => + { + Some(data_amount) + } + _ => None, + }) + .await; + + tokio::select! { + _ = bob_done_rx => { + return data_amount; + } + _ = alice.loop_on_next() => { + unreachable!(); + } + } + }; + + let bob_task = async move { + bob.wait(|event| match event { + SwarmEvent::ExternalAddrExpired { address } if address == bob_test_addr => Some(()), + _ => None, + }) + .await; + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + tested_addr: Some(tested_addr), + data_amount, + server, + result: Err(_), + })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(data_amount), + _ => None, + }) + .await; + bob_done_tx.send(()).unwrap(); + data_amount + }; + let (alice_amount, bob_amount) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_amount, bob_amount); } #[tokio::test] @@ -62,49 +131,99 @@ async fn dial_back_to_non_libp2p() { .with_env_filter(EnvFilter::from_default_env()) .try_init(); let (mut alice, mut bob) = bootstrap().await; + let alice_peer_id = *alice.local_peer_id(); for addr_str in [ - "/dns4/umgefahren.xyz/tcp/809", "/ip4/169.150.247.38/tcp/32", "/ip6/2400:52e0:1e02::1187:1/tcp/1000", ] { let addr: Multiaddr = addr_str.parse().unwrap(); - bob.behaviour_mut().autonat.inject_test_addr(addr.clone()); + let bob_addr = addr.clone(); + bob.behaviour_mut() + .autonat + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(mut peers), - .. - }], - [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::GotDialDataReq { - addr: addr_0, - num_bytes: req_num, - }, - .. - })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::CompletedDialData { - addr: addr_1, - num_bytes: done_num, - }, - .. - })), SwarmEvent::ExternalAddrExpired { .. }], - ) => { - let (peer_addr, _) = peers.pop().unwrap(); - let cleaned_addr: Multiaddr = peer_addr - .iter() - .filter(|p| !matches!(p, Protocol::P2p(_))) - .collect(); - assert_eq!(addr, cleaned_addr); - assert_eq!(addr_0, cleaned_addr); - assert_eq!(addr_1, cleaned_addr); - assert_eq!(req_num, done_num); + let alice_task = async move { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|p| (p, connection_id)), + _ => None, + }) + .await; + let mut outgoing_conn_error = alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::Transport(peers), + } if connection_id == alice_conn_id && peer_id == alice_dialing_peer => { + Some(peers) + } + _ => None, + }) + .await; + + if let Some((multiaddr, TransportError::Other(o))) = outgoing_conn_error.pop() { + assert_eq!( + multiaddr, + addr.clone().with_p2p(alice_dialing_peer).unwrap() + ); + let error_string = o.to_string(); + assert!( + error_string.contains("Connection refused"), + "Coorect error string: {error_string}" + ); + } else { + panic!("No outgoing connection errors"); } - e => panic!("unknown events {e:#?}"), - } + + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + all_addrs, + tested_addr: Some(tested_addr), + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![addr.clone()] + && tested_addr == addr + && alice_dialing_peer == client => + { + Some(data_amount) + } + _ => None, + }) + .await; + (alice, data_amount) + }; + let bob_task = async move { + bob.wait(|event| match event { + SwarmEvent::ExternalAddrExpired { address } if address == bob_addr => Some(()), + _ => None, + }) + .await; + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + tested_addr: Some(tested_addr), + data_amount, + server, + result: Err(_), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(data_amount), + _ => None, + }) + .await; + (bob, data_amount) + }; + + let ((a, alice_data_amount), (b, bob_data_amount)) = tokio::join!(alice_task, bob_task); + (alice, bob) = (a, b); + assert_eq!(alice_data_amount, bob_data_amount); } } @@ -115,44 +234,104 @@ async fn dial_back_to_not_supporting() { .try_init(); let (mut alice, mut bob) = bootstrap().await; + let alice_peer_id = *alice.local_peer_id(); - let mut hannes = new_dummy().await; + let (bob_done_tx, bob_done_rx) = oneshot::channel(); + + let hannes = new_dummy().await; + let hannes_peer_id = *hannes.local_peer_id(); let unreachable_address = hannes.external_addresses().next().unwrap().clone(); + let bob_unreachable_address = unreachable_address.clone(); bob.behaviour_mut() .autonat - .inject_test_addr(unreachable_address.clone()); + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { + addr: &unreachable_address, + }, + )); - let handler = tokio::spawn(async move { - loop { - hannes.next_swarm_event().await; - } - }); + let handler = tokio::spawn(async move { hannes.loop_on_next().await }); - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::GotDialDataReq { - addr: addr_0, - num_bytes: req_num, - }, - .. - })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::CompletedDialData { - addr: addr_1, - num_bytes: done_num, - }, - .. - })), SwarmEvent::ExternalAddrExpired { .. }], - ) => { - assert_eq!(addr_0, unreachable_address); - assert_eq!(addr_1, unreachable_address); - assert_eq!(req_num, done_num); + let alice_task = async move { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|p| (p, connection_id)), + _ => None, + }) + .await; + alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::WrongPeerId { obtained, .. }, + } if connection_id == alice_conn_id + && peer_id == alice_dialing_peer + && obtained == hannes_peer_id => + { + Some(()) + } + _ => None, + }) + .await; + + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + all_addrs, + tested_addr: Some(tested_addr), + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![unreachable_address.clone()] + && tested_addr == unreachable_address + && alice_dialing_peer == client => + { + Some(data_amount) + } + _ => None, + }) + .await; + tokio::select! { + _ = bob_done_rx => { + return data_amount; + } + _ = alice.loop_on_next() => { + unreachable!(); + } } - e => panic!("unknown events {e:#?}"), - } + }; + + let bob_task = async move { + bob.wait(|event| match event { + SwarmEvent::ExternalAddrExpired { address } if address == bob_unreachable_address => { + Some(()) + } + _ => None, + }) + .await; + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + tested_addr: Some(tested_addr), + data_amount, + server, + result: Err(_), + })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { + Some(data_amount) + } + _ => None, + }) + .await; + bob_done_tx.send(()).unwrap(); + data_amount + }; + + let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_data_amount, bob_data_amount); handler.abort(); } @@ -171,7 +350,10 @@ async fn new_server() -> Swarm { async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { - autonat: libp2p_autonatv2::client::Behaviour::default(), + autonat: libp2p_autonatv2::client::Behaviour::new( + OsRng, + Config::default().with_recheck_interval(Duration::from_nanos(0)), + ), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), identity.public().clone(), @@ -211,44 +393,124 @@ async fn bootstrap() -> (Swarm, Swarm) { let cor_server_peer = alice.local_peer_id().clone(); let mut bob = new_client().await; let cor_client_peer = bob.local_peer_id().clone(); + + let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); + let alice_bob_external_addrs = bob_external_addrs.clone(); + bob.connect(&mut alice).await; - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { - peer_id: client_peer_sent, - })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( - libp2p_identify::Event::Received { - peer_id: client_peer_recv, + let (tx, rx) = oneshot::channel(); + + let alice_task = async move { + let _ = alice + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), + _ => None, + }) + .await; + + let (dialed_peer_id, dialed_connection_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, .. - }, - )), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { - peer_id: server_peer_sent, - })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( - libp2p_identify::Event::Received { - peer_id: server_peer_recv, + } => peer_id.map(|peer_id| (peer_id, connection_id)), + _ => None, + }) + .await; + + assert_eq!(dialed_peer_id, cor_client_peer); + + let _ = alice + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + peer_id, + connection_id, .. - }, - ))], - ) => { - assert_eq!(server_peer_sent, cor_server_peer); - assert_eq!(client_peer_sent, cor_client_peer); - assert_eq!(server_peer_recv, cor_server_peer); - assert_eq!(client_peer_recv, cor_client_peer); - } - e => panic!("unexpected events: {e:#?}"), - } + } if peer_id == dialed_peer_id + && peer_id == cor_client_peer + && connection_id == dialed_connection_id => + { + Some(()) + } + _ => None, + }) + .await; - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], - ) => { - assert_eq!(addr_new, addr_snd); - assert_eq!(addr_snd, addr_ok); - } - e => panic!("unknown events {e:#?}"), - } - (alice, bob) + let server::StatusUpdate { + all_addrs, + tested_addr, + client, + data_amount, + result, + } = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { + Some(status_update) + } + _ => None, + }) + .await; + + assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(client, cor_client_peer); + assert_eq!(&all_addrs[..], &bob_external_addrs[..]); + assert!(result.is_ok(), "Result: {result:?}"); + + rx.await.unwrap(); + alice + }; + + let bob_task = async move { + let address_candidate = bob + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; + let incoming_conn_id = bob + .wait(|event| match event { + SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), + _ => None, + }) + .await; + + let _ = bob.wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }); + + let client::StatusUpdate { + tested_addr, + data_amount, + server, + result, + } = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { + Some(status_update) + } + SwarmEvent::ExternalAddrConfirmed { address } => { + assert_eq!(address, address_candidate); + None + } + _ => None, + }) + .await; + assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(server, cor_server_peer); + assert!(result.is_ok(), "Result is {result:?}"); + + tx.send(()).unwrap(); + bob + }; + + tokio::join!(alice_task, bob_task) } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index aea49c376ab..74725b148e1 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -595,7 +595,9 @@ where } self.behaviour - .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { listener_id })); + .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { + listener_id, + })); Ok(()) } @@ -606,7 +608,9 @@ where /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. pub fn add_external_address(&mut self, a: Multiaddr) { self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &a })); + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &a, + })); self.confirmed_external_addr.insert(a); } @@ -1042,9 +1046,9 @@ where ); let addrs = self.listened_addrs.remove(&listener_id).unwrap_or_default(); for addr in addrs.iter() { - self.behaviour.on_swarm_event( - FromSwarm::ExpiredListenAddr(ExpiredListenAddr { listener_id, addr }) - ); + self.behaviour.on_swarm_event(FromSwarm::ExpiredListenAddr( + ExpiredListenAddr { listener_id, addr }, + )); } self.behaviour .on_swarm_event(FromSwarm::ListenerClosed(ListenerClosed { @@ -2131,9 +2135,9 @@ mod tests { {} match swarm2.poll_next_unpin(cx) { - Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { peer_id, error, .. })) => { - Poll::Ready((peer_id, error)) - } + Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { + peer_id, error, .. + })) => Poll::Ready((peer_id, error)), Poll::Ready(x) => panic!("unexpected {x:?}"), Poll::Pending => Poll::Pending, } @@ -2199,7 +2203,9 @@ mod tests { return Poll::Ready(Ok(())); } } - Poll::Ready(Some(SwarmEvent::IncomingConnectionError { local_addr, .. })) => { + Poll::Ready(Some(SwarmEvent::IncomingConnectionError { + local_addr, .. + })) => { assert!(!got_inc_err); assert_eq!(local_addr, local_address); got_inc_err = true; @@ -2320,9 +2326,10 @@ mod tests { // This constitutes a fairly typical error for chained transports. let error = DialError::Transport(vec![( "/ip4/127.0.0.1/tcp/80".parse().unwrap(), - TransportError::Other( - io::Error::new(io::ErrorKind::Other, MemoryTransportError::Unreachable) - ), + TransportError::Other(io::Error::new( + io::ErrorKind::Other, + MemoryTransportError::Unreachable, + )), )]); let string = format!("{error}"); diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 79f95d2df29..fec7b0802c5 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -802,7 +802,9 @@ mod tests { .unwrap() ), Ok(SocketAddr::new( - IpAddr::V6(Ipv6Addr::new(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,)), + IpAddr::V6(Ipv6Addr::new( + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + )), 8080, )) ); @@ -1275,11 +1277,10 @@ mod tests { #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); assert!(rt.block_on(cycle_listeners::())); } } @@ -1312,11 +1313,10 @@ mod tests { } #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); rt.block_on(async { test::(); }); From da2a7909a32e336705d78da7a43f7532a4f31805 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:51:50 +0100 Subject: [PATCH 16/97] Implement all suggestions from the PR review --- protocols/autonatv2/Cargo.toml | 8 +- protocols/autonatv2/src/client/behaviour.rs | 37 ++--- protocols/autonatv2/src/client/handler.rs | 3 - .../autonatv2/src/client/handler/dial_back.rs | 28 ++-- .../src/client/handler/dial_request.rs | 42 +++--- protocols/autonatv2/src/lib.rs | 7 +- protocols/autonatv2/src/request_response.rs | 127 +++++++++++------- protocols/autonatv2/src/server/behaviour.rs | 24 +--- .../autonatv2/src/server/handler/dial_back.rs | 26 +--- .../src/server/handler/dial_request.rs | 26 ++-- 10 files changed, 149 insertions(+), 179 deletions(-) diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 55b778c6ef9..4944502c3e1 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -13,7 +13,7 @@ quick-protobuf-codec = { workspace = true } asynchronous-codec = "0.7" libp2p-core = { workspace = true } rand_core = "0.6" -rand = { version = "0.8", optional = true } +rand = "0.8" libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } futures-bounded = { workspace = true } @@ -28,7 +28,7 @@ unsigned-varint = { workspace = true, features = ["futures"] } futures-timer = "3.0.2" [dev-dependencies] -tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] } +tokio = { version = "1", features = ["macros", "rt", "sync"] } libp2p-swarm-test = { workspace = true } libp2p-identify = { workspace = true } libp2p-swarm = { workspace = true, features = ["macros"] } @@ -36,7 +36,3 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"]} [lints] workspace = true - -[features] -default = ["rand"] -rand = ["dep:rand"] diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 47f6ece4d07..859159dcb81 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -22,7 +22,7 @@ use crate::{global_only::IpExt, request_response::DialRequest}; use super::handler::{ dial_back, dial_request::{self, StatusUpdate}, - Handler, TestEnd, + TestEnd, }; #[derive(Debug, Clone, Copy)] @@ -89,7 +89,7 @@ impl NetworkBehaviour for Behaviour where R: RngCore + 'static, { - type ConnectionHandler = Handler; + type ConnectionHandler = Either; type ToSwarm = StatusUpdate; @@ -123,9 +123,7 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - if !self.already_tested.contains(addr) { - *self.address_candidates.entry(addr.clone()).or_default() += 1; - } + *self.address_candidates.entry(addr.clone()).or_default() += 1; } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { self.address_candidates.remove(addr); @@ -144,7 +142,6 @@ where connection_id, .. }) => { - tracing::trace!("connection with {peer_id:?} closed"); self.handle_no_connection(peer_id, connection_id); } FromSwarm::DialFailure(DialFailure { @@ -163,7 +160,7 @@ where &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: ::ToBehaviour, + event: ::ToBehaviour, ) { if matches!(event, Either::Left(_)) { self.peers_to_handlers @@ -171,16 +168,13 @@ where .or_insert(connection_id); } match event { - Either::Right(Ok(nonce)) => { + Either::Right(nonce) => { if self.pending_nonces.remove(&nonce) { tracing::trace!("Received pending nonce from {peer_id:?}"); } else { tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); } } - Either::Right(Err(err)) => { - tracing::debug!("Dial back failed: {:?}", err); - } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { if !self.known_servers.contains(&peer_id) { self.known_servers.push(peer_id); @@ -228,7 +222,8 @@ where fn poll( &mut self, cx: &mut Context<'_>, - ) -> Poll::FromBehaviour>> { + ) -> Poll::FromBehaviour>> + { let pending_event = self.poll_pending_events(); if pending_event.is_ready() { return pending_event; @@ -237,12 +232,19 @@ where && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { - let mut entries = self.address_candidates.drain().collect::>(); + let mut entries = self + .address_candidates + .iter() + .filter(|(addr, _)| !self.already_tested.contains(addr)) + .collect::>(); + if entries.is_empty() { + return Poll::Pending; + } entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries .into_iter() .rev() - .map(|(addr, _)| addr) + .map(|(addr, _)| addr.clone()) .take(self.config.max_addrs_count) .collect::>(); self.already_tested.extend(addrs.iter().cloned()); @@ -296,7 +298,7 @@ where self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, handler: NotifyHandler::One(*conn_id), - event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), + event: Either::Left(req), }); } else { tracing::debug!( @@ -317,7 +319,10 @@ where fn poll_pending_events( &mut self, ) -> Poll< - ToSwarm<::ToSwarm, ::FromBehaviour>, + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, > { if let Some(event) = self.pending_events.pop_front() { return Poll::Ready(event); diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 0776e45e5bd..711a6599559 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,7 +1,6 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; -use either::Either; use std::{ fmt::{Display, Formatter}, sync::Arc, @@ -15,8 +14,6 @@ use self::dial_request::InternalError; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub(crate) type Handler = Either; - #[derive(Clone, Debug)] pub struct Error { pub(crate) internal: Arc, diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 906ba6a35e8..08704e2e13c 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,5 +1,4 @@ use std::{ - convert::identity, io, task::{Context, Poll}, }; @@ -11,13 +10,12 @@ use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; +use void::Void; -use crate::{request_response::DialBack, Nonce}; +use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; -pub(crate) type ToBehaviour = io::Result; - pub struct Handler { inbound: FuturesSet>, } @@ -31,15 +29,15 @@ impl Handler { } impl ConnectionHandler for Handler { - type FromBehaviour = (); - type ToBehaviour = ToBehaviour; + type FromBehaviour = Void; + type ToBehaviour = Nonce; type InboundProtocol = ReadyUpgrade; type OutboundProtocol = DeniedUpgrade; type InboundOpenInfo = (); type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()) } fn poll( @@ -49,11 +47,13 @@ impl ConnectionHandler for Handler { ConnectionHandlerEvent, > { if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - result - .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) - .and_then(identity), - )); + match result { + Ok(Ok(nonce)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) + } + Ok(Err(err)) => tracing::debug!("Dial back handler failed with: {err:?}"), + Err(err) => tracing::debug!("Dial back handler timed out with: {err:?}"), + } } Poll::Pending } @@ -83,10 +83,6 @@ impl ConnectionHandler for Handler { _ => {} } } - - fn connection_keep_alive(&self) -> bool { - false - } } async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index d3a13218ace..db152af63d8 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -33,7 +33,7 @@ use crate::{ DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, - REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, + REQUEST_PROTOCOL_NAME, }; use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; @@ -89,11 +89,6 @@ pub struct StatusUpdate { pub result: Result<(), crate::client::handler::Error>, } -#[derive(Debug)] -pub enum FromBehaviour { - PerformRequest(DialRequest), -} - pub struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< @@ -134,7 +129,7 @@ impl Handler { self.queued_streams.push_back(tx); self.queued_events .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()), }); if self .outbound @@ -152,16 +147,11 @@ impl Handler { } impl ConnectionHandler for Handler { - type FromBehaviour = FromBehaviour; - + type FromBehaviour = DialRequest; type ToBehaviour = ToBehaviour; - type InboundProtocol = DeniedUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { @@ -196,11 +186,7 @@ impl ConnectionHandler for Handler { } fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - match event { - FromBehaviour::PerformRequest(req) => { - self.perform_request(req); - } - } + self.perform_request(event); } fn on_connection_event( @@ -272,6 +258,7 @@ async fn start_substream_handle( let mut data_amount = 0; let mut checked_addr_idx = None; let addrs = dial_request.addrs.clone(); + assert_ne!(addrs, vec![]); let res = handle_substream( dial_request, substream, @@ -298,10 +285,8 @@ async fn handle_substream( checked_addr_idx: &mut Option, ) -> Result { let mut coder = Coder::new(substream); - coder - .send_request(Request::Dial(dial_request.clone())) - .await?; - match coder.next_response().await? { + coder.send(Request::Dial(dial_request.clone())).await?; + match coder.next().await? { Response::Data(DialDataRequest { addr_idx, num_bytes, @@ -326,7 +311,7 @@ async fn handle_substream( } *checked_addr_idx = Some(addr_idx); send_aap_data(&mut coder, num_bytes, data_amount).await?; - if let Response::Dial(dial_response) = coder.next_response().await? { + if let Response::Dial(dial_response) = coder.next().await? { *checked_addr_idx = Some(dial_response.addr_idx); coder.close().await?; test_end_from_dial_response(dial_request, dial_response) @@ -396,10 +381,17 @@ where .take(count_full) .chain(once(partial_len)) .filter(|e| *e > 0) - .map(|data_count| (data_count, Request::Data(DialDataResponse { data_count }))) + .map(|data_count| { + ( + data_count, + Request::Data( + DialDataResponse::new(data_count).expect("data count is unexpectedly too big"), + ), + ) + }) { *data_amount += data_count; - substream.send_request(req).await?; + substream.send(req).await?; } Ok(()) } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index 00023a8b9c3..cca22be465c 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -1,4 +1,3 @@ -use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; pub mod client; @@ -11,9 +10,5 @@ pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); pub(crate) const DIAL_BACK_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-back"); -pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = - ReadyUpgrade::new(REQUEST_PROTOCOL_NAME); -pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = - ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); -pub type Nonce = u64; +type Nonce = u64; diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 5716863dee6..c92003eba8a 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -18,15 +18,13 @@ pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; -macro_rules! new_io_invalid_data_err { - ($msg:expr) => { - io::Error::new(io::ErrorKind::InvalidData, $msg) - }; +fn new_io_invalid_data_err(msg: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, msg.into()) } -macro_rules! check_existence { +macro_rules! ok_or_invalid_data { ($field:ident) => { - $field.ok_or_else(|| new_io_invalid_data_err!(concat!(stringify!($field), " is missing"))) + $field.ok_or_else(|| new_io_invalid_data_err(concat!(stringify!($field), " is missing"))) }; } @@ -43,9 +41,8 @@ where inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), } } - pub(crate) async fn close(self) -> io::Result<()> { - let mut stream = self.inner.into_inner(); - stream.close().await?; + pub(crate) async fn close(mut self) -> io::Result<()> { + self.inner.close().await?; Ok(()) } } @@ -54,37 +51,37 @@ impl Coder where I: AsyncRead + Unpin, { + pub(crate) async fn next(&mut self) -> io::Result + where + proto::Message: TryInto, + io::Error: From, + { + Ok(self.next_msg().await?.try_into()?) + } + async fn next_msg(&mut self) -> io::Result { self.inner .next() .await - .ok_or(io::Error::new(ErrorKind::Other, "no request to read"))? + .ok_or(io::Error::new( + ErrorKind::UnexpectedEof, + "no request to read", + ))? .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) } - pub(crate) async fn next_request(&mut self) -> io::Result { - Request::from_proto(self.next_msg().await?) - } - pub(crate) async fn next_response(&mut self) -> io::Result { - Response::from_proto(self.next_msg().await?) - } } impl Coder where I: AsyncWrite + Unpin, { - async fn send_msg(&mut self, msg: proto::Message) -> io::Result<()> { - self.inner.send(msg).await?; + pub(crate) async fn send(&mut self, msg: M) -> io::Result<()> + where + M: Into, + { + self.inner.send(msg.into()).await?; Ok(()) } - - pub(crate) async fn send_request(&mut self, request: Request) -> io::Result<()> { - self.send_msg(request.into_proto()).await - } - - pub(crate) async fn send_response(&mut self, response: Response) -> io::Result<()> { - self.send_msg(response.into_proto()).await - } } #[derive(Debug, Clone, PartialEq)] @@ -101,36 +98,54 @@ pub struct DialRequest { #[derive(Debug, Clone, PartialEq)] pub(crate) struct DialDataResponse { - pub(crate) data_count: usize, + data_count: usize, } -impl Request { - pub(crate) fn from_proto(msg: proto::Message) -> io::Result { +impl DialDataResponse { + pub(crate) fn new(data_count: usize) -> Option { + if data_count <= DATA_FIELD_LEN_UPPER_BOUND { + Some(Self { data_count }) + } else { + None + } + } + + pub(crate) fn get_data_count(&self) -> usize { + self.data_count + } +} + +impl TryFrom for Request { + type Error = io::Error; + + fn try_from(msg: proto::Message) -> Result { match msg.msg { proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { - let addrs: Vec = addrs + let addrs = addrs .into_iter() .map(|e| e.to_vec()) .map(|e| { Multiaddr::try_from(e).map_err(|err| { - new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + new_io_invalid_data_err(format!("invalid multiaddr: {}", err)) }) }) .collect::, io::Error>>()?; - let nonce = check_existence!(nonce)?; + let nonce = ok_or_invalid_data!(nonce)?; Ok(Self::Dial(DialRequest { addrs, nonce })) } proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { - let data_count = check_existence!(data)?.len(); + let data_count = ok_or_invalid_data!(data)?.len(); Ok(Self::Data(DialDataResponse { data_count })) } - _ => Err(new_io_invalid_data_err!( - "invalid message type, expected dialRequest or dialDataResponse" + _ => Err(new_io_invalid_data_err( + "expected dialResponse or dialDataRequest", )), } } +} - pub(crate) fn into_proto(self) -> proto::Message { +impl Into for Request { + fn into(self) -> proto::Message { match self { Request::Dial(DialRequest { addrs, nonce }) => { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); @@ -177,18 +192,20 @@ pub(crate) struct DialResponse { pub(crate) dial_status: proto::DialStatus, } -impl Response { - pub(crate) fn from_proto(msg: proto::Message) -> std::io::Result { +impl TryFrom for Response { + type Error = io::Error; + + fn try_from(msg: proto::Message) -> Result { match msg.msg { proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { status, addrIdx, dialStatus, }) => { - let status = check_existence!(status)?; - let addr_idx = check_existence!(addrIdx)? as usize; - let dial_status = check_existence!(dialStatus)?; - Ok(Self::Dial(DialResponse { + let status = ok_or_invalid_data!(status)?; + let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; + let dial_status = ok_or_invalid_data!(dialStatus)?; + Ok(Response::Dial(DialResponse { status, addr_idx, dial_status, @@ -198,20 +215,22 @@ impl Response { addrIdx, numBytes, }) => { - let addr_idx = check_existence!(addrIdx)? as usize; - let num_bytes = check_existence!(numBytes)? as usize; + let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; + let num_bytes = ok_or_invalid_data!(numBytes)? as usize; Ok(Self::Data(DialDataRequest { addr_idx, num_bytes, })) } - _ => Err(new_io_invalid_data_err!( - "invalid message type, expected dialResponse or dialDataRequest" + _ => Err(new_io_invalid_data_err( + "invalid message type, expected dialResponse or dialDataRequest", )), } } +} - pub(crate) fn into_proto(self) -> proto::Message { +impl Into for Response { + fn into(self) -> proto::Message { match self { Self::Dial(DialResponse { status, @@ -260,11 +279,11 @@ impl DialBack { .next() .await .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; - let nonce = check_existence!(nonce)?; + let nonce = ok_or_invalid_data!(nonce)?; Ok(Self { nonce }) } - pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { + async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = proto::DialBack { nonce: Some(self.nonce), }; @@ -275,6 +294,12 @@ impl DialBack { } } +pub(crate) async fn write_nonce(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let dial_back = DialBack { nonce }; + dial_back.write_into(&mut stream).await?; + stream.close().await +} + #[cfg(test)] mod tests { use crate::generated::structs::{ @@ -323,13 +348,13 @@ mod tests { data_count: thread_rng().gen_range(0..4000), }); all_req.push(data_request.clone()); - coder.send_request(data_request.clone()).await.unwrap(); + coder.send(data_request.clone()).await.unwrap(); } let inner = coder.inner.into_inner(); inner.set_position(0); let mut coder = Coder::new(inner); for i in 0..100 { - let read_data_request = coder.next_request().await.unwrap(); + let read_data_request: Request = coder.next().await.unwrap(); assert_eq!(read_data_request, all_req[i]); } } diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 9480a113571..fce414fe667 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; -use super::handler::{ +use crate::server::handler::{ dial_back, dial_request::{self, DialBackCommand}, Handler, @@ -51,21 +51,6 @@ where rng, } } - - fn poll_pending_events( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - > { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - Poll::Pending - } } impl NetworkBehaviour for Behaviour @@ -154,11 +139,10 @@ where fn poll( &mut self, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll as ConnectionHandler>::FromBehaviour>> { - let pending_event = self.poll_pending_events(cx); - if pending_event.is_ready() { - return pending_event; + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); } Poll::Pending } diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 8a497dc1f84..682a8b19615 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use futures::{AsyncWrite, AsyncWriteExt}; +use futures::AsyncWrite; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; +use crate::{request_response::write_nonce, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -70,7 +70,7 @@ impl ConnectionHandler for Handler { if let Some(cmd) = self.pending_nonce.take() { self.requested_substream_nonce = Some(cmd); return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(DIAL_BACK_UPGRADE, ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()), }); } Poll::Pending @@ -104,11 +104,7 @@ impl ConnectionHandler for Handler { } } ConnectionEvent::DialUpgradeError(DialUpgradeError { - error: StreamUpgradeError::NegotiationFailed, - .. - }) - | ConnectionEvent::DialUpgradeError(DialUpgradeError { - error: StreamUpgradeError::Timeout, + error: StreamUpgradeError::NegotiationFailed | StreamUpgradeError::Timeout, .. }) => { if let Some(cmd) = self.requested_substream_nonce.take() { @@ -121,14 +117,14 @@ impl ConnectionHandler for Handler { } async fn perform_dial_back( - mut stream: impl AsyncWrite + Unpin, + stream: impl AsyncWrite + Unpin, DialBackCommand { nonce, back_channel, .. }: DialBackCommand, ) -> io::Result<()> { - let res = perform_dial_back_inner(&mut stream, nonce) + let res = write_nonce(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| DialBackRes::Ok) @@ -138,13 +134,3 @@ async fn perform_dial_back( .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; Ok(()) } - -async fn perform_dial_back_inner( - mut stream: impl AsyncWrite + Unpin, - nonce: Nonce, -) -> io::Result<()> { - let dial_back = DialBack { nonce }; - dial_back.write_into(&mut stream).await?; - stream.close().await?; - Ok(()) -} diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index db1e6b284e6..92880744a67 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -22,14 +22,12 @@ use libp2p_swarm::{ }; use rand_core::RngCore; -use crate::request_response::Coder; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{ - DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, - }, - Nonce, REQUEST_UPGRADE, + request_response::{DialDataRequest, DialRequest, DialResponse, Request, Response}, + Nonce, }; +use crate::{request_response::Coder, REQUEST_PROTOCOL_NAME}; #[derive(Clone, Debug)] pub struct StatusUpdate { @@ -104,7 +102,7 @@ where type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(REQUEST_UPGRADE, ()) + SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()) } fn poll( @@ -178,10 +176,6 @@ where _ => {} } } - - fn connection_keep_alive(&self) -> bool { - true - } } enum HandleFail { @@ -235,7 +229,7 @@ where I: AsyncRead + AsyncWrite + Unpin, { let DialRequest { mut addrs, nonce } = match coder - .next_request() + .next() .await .map_err(|_| HandleFail::InternalError(0))? { @@ -253,19 +247,19 @@ where let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); let mut rem_data = dial_data_request.num_bytes; coder - .send_response(Response::Data(dial_data_request)) + .send(Response::Data(dial_data_request)) .await .map_err(|_| HandleFail::InternalError(idx))?; while rem_data > 0 { - let DialDataResponse { data_count } = match coder - .next_request() + let data_count = match coder + .next() .await .map_err(|_e| HandleFail::InternalError(idx))? { Request::Dial(_) => { return Err(HandleFail::RequestRejected); } - Request::Data(dial_data_response) => dial_data_response, + Request::Data(dial_data_response) => dial_data_response.get_data_count(), }; rem_data = rem_data.saturating_sub(data_count); *data_amount += data_count; @@ -322,7 +316,7 @@ async fn handle_request( ) .await .unwrap_or_else(|e| e.into()); - coder.send_response(Response::Dial(response)).await?; + coder.send(Response::Dial(response)).await?; coder.close().await?; Ok(()) } From 101a3251efdb79a7e5c4ad072e270f35e9958874 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 17:17:55 +0100 Subject: [PATCH 17/97] Implement latest suggestions --- protocols/autonatv2/src/client.rs | 2 +- protocols/autonatv2/src/client/behaviour.rs | 69 +++++- protocols/autonatv2/src/client/handler.rs | 41 +--- .../src/client/handler/dial_request.rs | 33 +-- protocols/autonatv2/src/request_response.rs | 12 +- protocols/autonatv2/src/server.rs | 2 +- protocols/autonatv2/src/server/behaviour.rs | 19 +- .../autonatv2/src/server/handler/dial_back.rs | 4 +- .../src/server/handler/dial_request.rs | 16 +- protocols/autonatv2/tests/autonatv2.rs | 227 ++++++++++++------ 10 files changed, 263 insertions(+), 162 deletions(-) diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index fc15af146f3..d3272512f35 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,5 +1,5 @@ mod behaviour; mod handler; +pub use behaviour::Event; pub use behaviour::{Behaviour, Config}; -pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 859159dcb81..49e0a0a15c9 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -16,12 +16,15 @@ use libp2p_swarm::{ }; use rand::{seq::SliceRandom, Rng}; use rand_core::{OsRng, RngCore}; +use std::fmt::{Debug, Display, Formatter}; +use std::sync::Arc; +use crate::client::handler::dial_request::InternalError; use crate::{global_only::IpExt, request_response::DialRequest}; use super::handler::{ dial_back, - dial_request::{self, StatusUpdate}, + dial_request::{self}, TestEnd, }; @@ -91,7 +94,7 @@ where { type ConnectionHandler = Either; - type ToSwarm = StatusUpdate; + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -337,6 +340,68 @@ impl Default for Behaviour { } } +pub struct Error { + pub(crate) internal: Arc, +} + +impl Error { + pub(crate) fn duplicate(&self) -> Self { + Self { + internal: Arc::clone(&self.internal), + } + } +} + +impl From for Error { + fn from(value: InternalError) -> Self { + Self { + internal: Arc::new(value), + } + } +} + +impl From> for Error { + fn from(value: Arc) -> Self { + Self { internal: value } + } +} + +impl From<&Arc> for Error { + fn from(value: &Arc) -> Self { + Self { + internal: Arc::clone(value), + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.internal, f) + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.internal, f) + } +} + +#[derive(Debug)] +pub struct Event { + /// The address that was selected for testing. + /// Is `None` in the case that the server respond with something unexpected. + pub tested_addr: Option, + /// The amount of data that was sent to the server. + /// Is 0 if it wasn't necessary to send any data. + /// Otherwise it's a number between 30.000 and 100.000. + pub data_amount: usize, + /// The peer id of the server that was selected for testing. + pub server: PeerId, + /// The result of the test. If the test was successful, this is `Ok(())`. + /// Otherwise it's an error. + pub result: Result<(), Error>, +} + fn addr_is_local(addr: &Multiaddr) -> bool { addr.iter().any(|c| match c { Protocol::Dns(addr) diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 711a6599559..ce1300a6a8b 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,48 +1,9 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; -use std::{ - fmt::{Display, Formatter}, - sync::Arc, - time::Duration, -}; +use std::time::Duration; pub(crate) use dial_request::TestEnd; -use self::dial_request::InternalError; - const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; - -#[derive(Clone, Debug)] -pub struct Error { - pub(crate) internal: Arc, -} - -impl From for Error { - fn from(value: InternalError) -> Self { - Self { - internal: Arc::new(value), - } - } -} - -impl From> for Error { - fn from(value: Arc) -> Self { - Self { internal: value } - } -} - -impl From<&Arc> for Error { - fn from(value: &Arc) -> Self { - Self { - internal: Arc::clone(value), - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.internal.fmt(f) - } -} diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index db152af63d8..f11eac4edac 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -26,11 +26,12 @@ use std::{ task::{Context, Poll}, }; -use crate::request_response::Coder; +use crate::client::behaviour::Error; use crate::{ + client::behaviour::Event, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ - DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, REQUEST_PROTOCOL_NAME, @@ -76,19 +77,11 @@ pub struct TestEnd { #[derive(Debug)] pub enum ToBehaviour { - TestCompleted(Result), - StatusUpdate(StatusUpdate), + TestCompleted(Result), + StatusUpdate(Event), PeerHasServerSupport, } -#[derive(Debug)] -pub struct StatusUpdate { - pub tested_addr: Option, - pub data_amount: usize, - pub server: PeerId, - pub result: Result<(), crate::client::handler::Error>, -} - pub struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< @@ -97,7 +90,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet>, queued_streams: VecDeque< oneshot::Sender< Result< @@ -106,8 +99,8 @@ pub struct Handler { >, >, >, - status_update_rx: mpsc::Receiver, - status_update_tx: mpsc::Sender, + status_update_rx: mpsc::Receiver, + status_update_tx: mpsc::Sender, server: PeerId, } @@ -248,8 +241,8 @@ async fn start_substream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, - mut status_update_tx: mpsc::Sender, -) -> Result { + mut status_update_tx: mpsc::Sender, +) -> Result { let substream = match substream_recv.await { Ok(Ok(substream)) => substream, Ok(Err(err)) => return Err(InternalError::from(err).into()), @@ -267,12 +260,12 @@ async fn start_substream_handle( ) .await .map_err(Arc::new) - .map_err(crate::client::handler::Error::from); - let status_update = StatusUpdate { + .map_err(crate::client::behaviour::Error::from); + let status_update = Event { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), data_amount, server, - result: res.as_ref().map(|_| ()).map_err(|e| e.clone()), + result: res.as_ref().map(|_| ()).map_err(|e| e.duplicate()), }; let _ = status_update_tx.send(status_update).await; res diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index c92003eba8a..026d7c57c5c 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -266,6 +266,12 @@ impl DialDataRequest { } } +pub(crate) async fn dial_back(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let dial_back = DialBack { nonce }; + dial_back.write_into(&mut stream).await?; + stream.close().await +} + const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { @@ -294,12 +300,6 @@ impl DialBack { } } -pub(crate) async fn write_nonce(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let dial_back = DialBack { nonce }; - dial_back.write_into(&mut stream).await?; - stream.close().await -} - #[cfg(test)] mod tests { use crate::generated::structs::{ diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs index ef746607559..e864893d73d 100644 --- a/protocols/autonatv2/src/server.rs +++ b/protocols/autonatv2/src/server.rs @@ -2,4 +2,4 @@ mod behaviour; mod handler; pub use behaviour::Behaviour; -pub use handler::dial_request::StatusUpdate; +pub use behaviour::StatusUpdate; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index fce414fe667..0a7bee427df 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -1,5 +1,6 @@ use std::{ collections::{HashMap, VecDeque}, + io, task::{Context, Poll}, }; @@ -13,6 +14,7 @@ use libp2p_swarm::{ }; use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; +use std::sync::Arc; use crate::server::handler::{ dial_back, @@ -59,7 +61,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = crate::server::handler::dial_request::StatusUpdate; + type ToSwarm = StatusUpdate; fn handle_established_inbound_connection( &mut self, @@ -147,3 +149,18 @@ where Poll::Pending } } + +#[derive(Debug)] +pub struct StatusUpdate { + /// All address that were submitted for testing. + pub all_addrs: Vec, + /// The address that was eventually tested. + /// This is `None` if the client send and unexpected message. + pub tested_addr: Option, + /// The peer id of the client that submitted addresses for testing. + pub client: PeerId, + /// The amount of data that was requested by the server and was transmitted. + pub data_amount: usize, + /// The result of the test. + pub result: Result<(), Arc>, +} diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 682a8b19615..06e7d36f305 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{request_response::write_nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::{request_response::dial_back, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -124,7 +124,7 @@ async fn perform_dial_back( .. }: DialBackCommand, ) -> io::Result<()> { - let res = write_nonce(stream, nonce) + let res = dial_back(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| DialBackRes::Ok) diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 92880744a67..6d660f31ad9 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -24,19 +24,10 @@ use rand_core::RngCore; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{DialDataRequest, DialRequest, DialResponse, Request, Response}, - Nonce, + request_response::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, + server::behaviour::StatusUpdate, + Nonce, REQUEST_PROTOCOL_NAME, }; -use crate::{request_response::Coder, REQUEST_PROTOCOL_NAME}; - -#[derive(Clone, Debug)] -pub struct StatusUpdate { - pub all_addrs: Vec, - pub tested_addr: Option, - pub client: PeerId, - pub data_amount: usize, - pub result: Result<(), Arc>, -} #[derive(Debug, PartialEq)] pub(crate) enum DialBackStatus { @@ -280,7 +271,6 @@ where err: DialBackStatus::DialErr, })?; - // TODO: add timeout let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; if dial_back != DialBackStatus::Ok { return Err(HandleFail::DialBack { diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index bc1c2b52c36..3024c801660 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -17,7 +17,119 @@ async fn confirm_successful() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); - let (_alice, _bob) = bootstrap().await; + let (mut alice, mut bob) = start_and_connect().await; + + let cor_server_peer = alice.local_peer_id().clone(); + let cor_client_peer = bob.local_peer_id().clone(); + let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); + let alice_bob_external_addrs = bob_external_addrs.clone(); + + let alice_task = async { + let _ = alice + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), + _ => None, + }) + .await; + + let (dialed_peer_id, dialed_connection_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + .. + } => peer_id.map(|peer_id| (peer_id, connection_id)), + _ => None, + }) + .await; + + assert_eq!(dialed_peer_id, cor_client_peer); + + let _ = alice + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + peer_id, + connection_id, + .. + } if peer_id == dialed_peer_id + && peer_id == cor_client_peer + && connection_id == dialed_connection_id => + { + Some(()) + } + _ => None, + }) + .await; + + let server::StatusUpdate { + all_addrs, + tested_addr, + client, + data_amount, + result, + } = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { + Some(status_update) + } + _ => None, + }) + .await; + + assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(client, cor_client_peer); + assert_eq!(&all_addrs[..], &bob_external_addrs[..]); + assert!(result.is_ok(), "Result: {result:?}"); + }; + + let bob_task = async { + let address_candidate = bob + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; + let incoming_conn_id = bob + .wait(|event| match event { + SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), + _ => None, + }) + .await; + + let _ = bob.wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }); + + let client::Event { + tested_addr, + data_amount, + server, + result, + } = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { + Some(status_update) + } + SwarmEvent::ExternalAddrConfirmed { address } => { + assert_eq!(address, address_candidate); + None + } + _ => None, + }) + .await; + assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(server, cor_server_peer); + assert!(result.is_ok(), "Result is {result:?}"); + }; + + tokio::join!(alice_task, bob_task); } #[tokio::test] @@ -39,7 +151,7 @@ async fn dial_back_to_unsupported_protocol() { let (bob_done_tx, bob_done_rx) = oneshot::channel(); - let alice_task = async move { + let alice_task = async { let (alice_dialing_peer, alice_conn_id) = alice .wait(|event| match event { SwarmEvent::Dialing { @@ -101,7 +213,7 @@ async fn dial_back_to_unsupported_protocol() { } }; - let bob_task = async move { + let bob_task = async { bob.wait(|event| match event { SwarmEvent::ExternalAddrExpired { address } if address == bob_test_addr => Some(()), _ => None, @@ -109,7 +221,7 @@ async fn dial_back_to_unsupported_protocol() { .await; let data_amount = bob .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), data_amount, server, @@ -145,7 +257,7 @@ async fn dial_back_to_non_libp2p() { NewExternalAddrCandidate { addr: &addr }, )); - let alice_task = async move { + let alice_task = async { let (alice_dialing_peer, alice_conn_id) = alice .wait(|event| match event { SwarmEvent::Dialing { @@ -199,9 +311,9 @@ async fn dial_back_to_non_libp2p() { _ => None, }) .await; - (alice, data_amount) + data_amount }; - let bob_task = async move { + let bob_task = async { bob.wait(|event| match event { SwarmEvent::ExternalAddrExpired { address } if address == bob_addr => Some(()), _ => None, @@ -209,7 +321,7 @@ async fn dial_back_to_non_libp2p() { .await; let data_amount = bob .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), data_amount, server, @@ -218,11 +330,10 @@ async fn dial_back_to_non_libp2p() { _ => None, }) .await; - (bob, data_amount) + data_amount }; - let ((a, alice_data_amount), (b, bob_data_amount)) = tokio::join!(alice_task, bob_task); - (alice, bob) = (a, b); + let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); assert_eq!(alice_data_amount, bob_data_amount); } } @@ -250,9 +361,9 @@ async fn dial_back_to_not_supporting() { }, )); - let handler = tokio::spawn(async move { hannes.loop_on_next().await }); + let handler = tokio::spawn(async { hannes.loop_on_next().await }); - let alice_task = async move { + let alice_task = async { let (alice_dialing_peer, alice_conn_id) = alice .wait(|event| match event { SwarmEvent::Dialing { @@ -305,7 +416,7 @@ async fn dial_back_to_not_supporting() { } }; - let bob_task = async move { + let bob_task = async { bob.wait(|event| match event { SwarmEvent::ExternalAddrExpired { address } if address == bob_unreachable_address => { Some(()) @@ -315,7 +426,7 @@ async fn dial_back_to_not_supporting() { .await; let data_amount = bob .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), data_amount, server, @@ -388,20 +499,21 @@ async fn new_dummy() -> Swarm { node } -async fn bootstrap() -> (Swarm, Swarm) { +async fn start_and_connect() -> (Swarm, Swarm) { let mut alice = new_server().await; - let cor_server_peer = alice.local_peer_id().clone(); let mut bob = new_client().await; - let cor_client_peer = bob.local_peer_id().clone(); - - let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); - let alice_bob_external_addrs = bob_external_addrs.clone(); bob.connect(&mut alice).await; + (alice, bob) +} - let (tx, rx) = oneshot::channel(); +async fn bootstrap() -> (Swarm, Swarm) { + let (mut alice, mut bob) = start_and_connect().await; - let alice_task = async move { + let cor_server_peer = alice.local_peer_id().clone(); + let cor_client_peer = bob.local_peer_id().clone(); + + let alice_task = async { let _ = alice .wait(|event| match event { SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), @@ -420,8 +532,6 @@ async fn bootstrap() -> (Swarm, Swarm) { }) .await; - assert_eq!(dialed_peer_id, cor_client_peer); - let _ = alice .wait(|event| match event { SwarmEvent::ConnectionEstablished { @@ -438,38 +548,20 @@ async fn bootstrap() -> (Swarm, Swarm) { }) .await; - let server::StatusUpdate { - all_addrs, - tested_addr, - client, - data_amount, - result, - } = alice + alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { - Some(status_update) - } + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(_)) => Some(()), _ => None, }) .await; - - assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); - assert_eq!(data_amount, 0); - assert_eq!(client, cor_client_peer); - assert_eq!(&all_addrs[..], &bob_external_addrs[..]); - assert!(result.is_ok(), "Result: {result:?}"); - - rx.await.unwrap(); - alice }; - let bob_task = async move { - let address_candidate = bob - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; + let bob_task = async { + bob.wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; let incoming_conn_id = bob .wait(|event| match event { SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), @@ -486,31 +578,14 @@ async fn bootstrap() -> (Swarm, Swarm) { _ => None, }); - let client::StatusUpdate { - tested_addr, - data_amount, - server, - result, - } = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { - Some(status_update) - } - SwarmEvent::ExternalAddrConfirmed { address } => { - assert_eq!(address, address_candidate); - None - } - _ => None, - }) - .await; - assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); - assert_eq!(data_amount, 0); - assert_eq!(server, cor_server_peer); - assert!(result.is_ok(), "Result is {result:?}"); - - tx.send(()).unwrap(); - bob + bob.wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), + SwarmEvent::ExternalAddrConfirmed { .. } => None, + _ => None, + }) + .await; }; - tokio::join!(alice_task, bob_task) + tokio::join!(alice_task, bob_task); + (alice, bob) } From bf2662a73356d74d4e9c2acb25fa5e4153ec8915 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 18:39:59 +0100 Subject: [PATCH 18/97] Implement old suggestions i overlooked --- protocols/autonatv2/src/client/behaviour.rs | 158 +++++++++--------- protocols/autonatv2/src/client/handler.rs | 4 - .../autonatv2/src/client/handler/dial_back.rs | 9 +- .../src/client/handler/dial_request.rs | 56 ++++--- protocols/autonatv2/src/lib.rs | 2 +- .../src/{request_response.rs => protocol.rs} | 4 +- .../autonatv2/src/server/handler/dial_back.rs | 2 +- .../src/server/handler/dial_request.rs | 2 +- 8 files changed, 122 insertions(+), 115 deletions(-) rename protocols/autonatv2/src/{request_response.rs => protocol.rs} (98%) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 49e0a0a15c9..08c9d27dd84 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -19,8 +19,8 @@ use rand_core::{OsRng, RngCore}; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; -use crate::client::handler::dial_request::InternalError; -use crate::{global_only::IpExt, request_response::DialRequest}; +use crate::{client::handler::dial_request::InternalError, Nonce}; +use crate::{global_only::IpExt, protocol::DialRequest}; use super::handler::{ dial_back, @@ -126,7 +126,7 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - *self.address_candidates.entry(addr.clone()).or_default() += 1; + self.inject_address_candiate(addr.clone()) } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { self.address_candidates.remove(addr); @@ -179,12 +179,10 @@ where } } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { - if !self.known_servers.contains(&peer_id) { - self.known_servers.push(peer_id); - } + self.inject_kown_server(peer_id); } Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { - dial_request: DialRequest { nonce, addrs }, + dial_request: DialRequest { nonce, .. }, reachable_addr, }))) => { if self.pending_nonces.remove(&nonce) { @@ -193,12 +191,6 @@ where ); return; } - self.pending_events.extend( - addrs - .into_iter() - .take_while(|addr| addr != &reachable_addr) - .map(ToSwarm::ExternalAddrExpired), - ); self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } @@ -211,6 +203,15 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrExpired(addr.clone())); } + dial_request::InternalError::InternalServer + | dial_request::InternalError::DataRequestTooLarge { .. } + | dial_request::InternalError::DataRequestTooSmall { .. } + | dial_request::InternalError::InvalidResponse + | dial_request::InternalError::ServerRejectedDialRequest + | dial_request::InternalError::InvalidReferencedAddress { .. } + | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { + self.handle_no_connection(peer_id, connection_id); + } _ => { tracing::debug!("Test failed: {:?}", err); } @@ -227,50 +228,13 @@ where cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { - let pending_event = self.poll_pending_events(); - if pending_event.is_ready() { - return pending_event; + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); } - if self.next_tick.poll_unpin(cx).is_ready() - && !self.known_servers.is_empty() - && !self.address_candidates.is_empty() - { - let mut entries = self - .address_candidates - .iter() - .filter(|(addr, _)| !self.already_tested.contains(addr)) - .collect::>(); - if entries.is_empty() { - return Poll::Pending; - } - entries.sort_unstable_by_key(|(_, count)| *count); - let addrs = entries - .into_iter() - .rev() - .map(|(addr, _)| addr.clone()) - .take(self.config.max_addrs_count) - .collect::>(); - self.already_tested.extend(addrs.iter().cloned()); - let peers = if self.known_servers.len() < self.config.test_server_count { - self.known_servers.clone() - } else { - self.known_servers - .choose_multiple(&mut self.rng, self.config.test_server_count) - .copied() - .collect() - }; - for peer in peers { - let nonce = self.rng.gen(); - let req = DialRequest { - nonce, - addrs: addrs.clone(), - }; - self.pending_nonces.insert(nonce); - self.submit_req_for_peer(peer, req); - } - let pending_event = self.poll_pending_events(); - if pending_event.is_ready() { - return pending_event; + if self.next_tick.poll_unpin(cx).is_ready() { + self.inject_address_candiate_test(); + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); } } Poll::Pending @@ -296,18 +260,74 @@ where } } - fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest) { + /// Injects a known server into the behaviour. It's mostly useful if you are not using identify + /// or for testing purposes. + pub fn inject_kown_server(&mut self, peer: PeerId) { + if !self.known_servers.contains(&peer) { + self.known_servers.push(peer); + } + } + + /// Inject a new address candidate into the behaviour. + pub fn inject_address_candiate(&mut self, addr: Multiaddr) { + *self.address_candidates.entry(addr).or_default() += 1; + } + + /// Inject an immediate test for all pending address candidates. + pub fn inject_address_candiate_test(&mut self) { + if self.known_servers.is_empty() || self.address_candidates.is_empty() { + return; + } + let mut entries = self + .address_candidates + .iter() + .filter(|(addr, _)| !self.already_tested.contains(addr)) + .map(|(addr, count)| (addr.clone(), *count)) + .collect::>(); + entries.sort_unstable_by_key(|(_, count)| *count); + let addrs = entries + .iter() + .rev() + .map(|(addr, _)| addr) + .take(self.config.max_addrs_count) + .cloned() + .collect::>(); + self.already_tested.extend(addrs.iter().cloned()); + let peers = if self.known_servers.len() < self.config.test_server_count { + self.known_servers.clone() + } else { + self.known_servers + .choose_multiple(&mut self.rng, self.config.test_server_count) + .copied() + .collect() + }; + for peer in peers { + let mut remaining_entries = entries.clone(); + let mut addrs = Vec::with_capacity(self.config.max_addrs_count); + while !remaining_entries.is_empty() && addrs.len() < self.config.max_addrs_count { + let addr = remaining_entries + .choose_weighted(&mut self.rng, |item| item.1) + .unwrap() + .0 + .clone(); + remaining_entries.retain(|(a, _)| a != &addr); + addrs.push(addr); + } + let nonce = self.rng.gen(); + let req = DialRequest { nonce, addrs }; + self.submit_req_for_peer(peer, req, nonce); + } + self.next_tick.reset(self.config.recheck_interval); + } + + fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest, nonce: Nonce) { + self.pending_nonces.insert(nonce); if let Some(conn_id) = self.peers_to_handlers.get(&peer) { self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, handler: NotifyHandler::One(*conn_id), event: Either::Left(req), }); - } else { - tracing::debug!( - "There should be a connection to {:?}, but there isn't", - peer - ); } } @@ -318,20 +338,6 @@ where } self.known_servers.retain(|p| p != &peer_id); } - - fn poll_pending_events( - &mut self, - ) -> Poll< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - > { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - Poll::Pending - } } impl Default for Behaviour { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index ce1300a6a8b..b30a10d8aae 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,9 +1,5 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; -use std::time::Duration; - pub(crate) use dial_request::TestEnd; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); -const MAX_CONCURRENT_REQUESTS: usize = 10; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 08704e2e13c..61d21fcf7ce 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,6 +1,7 @@ use std::{ io, task::{Context, Poll}, + time::Duration, }; use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; @@ -12,9 +13,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; - -use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; +use crate::{protocol::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; pub struct Handler { inbound: FuturesSet>, @@ -23,7 +22,7 @@ pub struct Handler { impl Handler { pub(crate) fn new() -> Self { Self { - inbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + inbound: FuturesSet::new(Duration::from_secs(5), 2), } } } @@ -78,7 +77,7 @@ impl ConnectionHandler for Handler { } } ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { - tracing::debug!("Dial back request failed: {:?}", error); + void::unreachable(error); } _ => {} } diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index f11eac4edac..bf709a8f0eb 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -24,21 +24,20 @@ use std::{ iter::{once, repeat}, sync::Arc, task::{Context, Poll}, + time::Duration, }; use crate::client::behaviour::Error; use crate::{ client::behaviour::Event, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{ + protocol::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, REQUEST_PROTOCOL_NAME, }; -use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; - #[derive(Debug, thiserror::Error)] pub(crate) enum InternalError { #[error("io error")] @@ -49,8 +48,6 @@ pub(crate) enum InternalError { DataRequestTooLarge { len: usize, max: usize }, #[error("data request too small: {len} (min: {min})")] DataRequestTooSmall { len: usize, min: usize }, - #[error("timeout")] - Timeout(#[from] futures_bounded::Timeout), #[error("server rejected dial request")] ServerRejectedDialRequest, #[error("server chose not to dial any provided address")] @@ -63,10 +60,6 @@ pub(crate) enum InternalError { UnableToConnectOnSelectedAddress { addr: Option }, #[error("server experienced failure during dial back on address: {addr:?}")] FailureDuringDialBack { addr: Option }, - #[error("error during substream upgrad")] - Substream( - #[from] StreamUpgradeError< as OutboundUpgradeSend>::Error>, - ), } #[derive(Debug)] @@ -109,7 +102,7 @@ impl Handler { let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + outbound: FuturesSet::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), status_update_tx, status_update_rx, @@ -126,7 +119,7 @@ impl Handler { }); if self .outbound - .try_push(start_substream_handle( + .try_push(start_stream_handle( self.server, req, rx, @@ -163,7 +156,7 @@ impl ConnectionHandler for Handler { if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( ToBehaviour::TestCompleted( - m.map_err(InternalError::Timeout) + m.map_err(|_| InternalError::Io(io::Error::from(io::ErrorKind::TimedOut))) .map_err(Into::into) .and_then(identity) .map_err(Into::into), @@ -196,9 +189,7 @@ impl ConnectionHandler for Handler { tracing::debug!("Dial request failed: {}", error); match self.queued_streams.pop_front() { Some(stream_tx) => { - if stream_tx.send(Err(error)).is_err() { - tracing::warn!("Failed to send stream to dead handler"); - } + let _ = stream_tx.send(Err(error)); } None => { tracing::warn!( @@ -232,10 +223,10 @@ impl ConnectionHandler for Handler { } } -async fn start_substream_handle( +async fn start_stream_handle( server: PeerId, dial_request: DialRequest, - substream_recv: oneshot::Receiver< + stream_recv: oneshot::Receiver< Result< Stream, StreamUpgradeError< as OutboundUpgradeSend>::Error>, @@ -243,16 +234,31 @@ async fn start_substream_handle( >, mut status_update_tx: mpsc::Sender, ) -> Result { - let substream = match substream_recv.await { + let substream = match stream_recv.await { Ok(Ok(substream)) => substream, - Ok(Err(err)) => return Err(InternalError::from(err).into()), + Ok(Err(StreamUpgradeError::Io(io))) => return Err(InternalError::from(io).into()), + Ok(Err(StreamUpgradeError::Timeout)) => { + return Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) + } + Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => { + return Err( + InternalError::Io(io::Error::new(io::ErrorKind::Other, upgrade_error)).into(), + ) + } + Ok(Err(StreamUpgradeError::NegotiationFailed)) => { + return Err(InternalError::Io(io::Error::new( + io::ErrorKind::Other, + "negotiation failed", + )) + .into()) + } Err(_) => return Err(InternalError::InternalServer.into()), }; let mut data_amount = 0; let mut checked_addr_idx = None; let addrs = dial_request.addrs.clone(); assert_ne!(addrs, vec![]); - let res = handle_substream( + let res = handle_stream( dial_request, substream, &mut data_amount, @@ -271,13 +277,13 @@ async fn start_substream_handle( res } -async fn handle_substream( +async fn handle_stream( dial_request: DialRequest, - substream: impl AsyncRead + AsyncWrite + Unpin, + stream: impl AsyncRead + AsyncWrite + Unpin, data_amount: &mut usize, checked_addr_idx: &mut Option, ) -> Result { - let mut coder = Coder::new(substream); + let mut coder = Coder::new(stream); coder.send(Request::Dial(dial_request.clone())).await?; match coder.next().await? { Response::Data(DialDataRequest { @@ -361,7 +367,7 @@ fn test_end_from_dial_response( } async fn send_aap_data( - substream: &mut Coder, + stream: &mut Coder, num_bytes: usize, data_amount: &mut usize, ) -> io::Result<()> @@ -384,7 +390,7 @@ where }) { *data_amount += data_count; - substream.send(req).await?; + stream.send(req).await?; } Ok(()) } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index cca22be465c..0fae928921b 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -3,7 +3,7 @@ use libp2p_swarm::StreamProtocol; pub mod client; mod generated; mod global_only; -pub(crate) mod request_response; +pub(crate) mod protocol; pub mod server; pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/protocol.rs similarity index 98% rename from protocols/autonatv2/src/request_response.rs rename to protocols/autonatv2/src/protocol.rs index 026d7c57c5c..a12e70bb7c3 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/protocol.rs @@ -284,7 +284,7 @@ impl DialBack { FramedRead::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) .next() .await - .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; let nonce = ok_or_invalid_data!(nonce)?; Ok(Self { nonce }) } @@ -305,7 +305,7 @@ mod tests { use crate::generated::structs::{ mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; - use crate::request_response::{Coder, DialDataResponse, Request}; + use crate::protocol::{Coder, DialDataResponse, Request}; use futures::io::Cursor; use rand::{thread_rng, Rng}; diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 06e7d36f305..c04c45c6fe8 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{request_response::dial_back, DIAL_BACK_PROTOCOL_NAME}; +use crate::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 6d660f31ad9..e04a3d8fc9b 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -24,7 +24,7 @@ use rand_core::RngCore; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, + protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::StatusUpdate, Nonce, REQUEST_PROTOCOL_NAME, }; From efdbe350cf0024dd32e5e3db3f79e585e5ff3b62 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:05:17 +0100 Subject: [PATCH 19/97] Resolve conflict --- protocols/autonatv2/src/client/behaviour.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 08c9d27dd84..b9bb3f0eeb4 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -284,6 +284,9 @@ where .filter(|(addr, _)| !self.already_tested.contains(addr)) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); + if entries.is_empty() { + return; + } entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries .iter() From c7350f901b7010d3198c82724489d472bd1a2238 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:20:09 +0100 Subject: [PATCH 20/97] Port dns transport --- transports/dns/src/lib.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index 3aeac4e4154..265d77ad997 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -148,9 +148,8 @@ pub mod tokio { use async_trait::async_trait; use futures::{future::BoxFuture, prelude::*}; use libp2p_core::{ - connection::Endpoint, multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -231,15 +230,12 @@ where self.inner.lock().remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.do_dial(addr, Endpoint::Dialer) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - self.do_dial(addr, Endpoint::Listener) + self.do_dial(addr, dial_opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { @@ -269,7 +265,7 @@ where fn do_dial( &mut self, addr: Multiaddr, - role_override: Endpoint, + dial_opts: DialOpts, ) -> Result< ::Dial, TransportError<::Error>, @@ -358,10 +354,7 @@ where tracing::debug!(address=%addr, "Dialing address"); let transport = inner.clone(); - let dial = match role_override { - Endpoint::Dialer => transport.lock().dial(addr), - Endpoint::Listener => transport.lock().dial_as_listener(addr), - }; + let dial = transport.lock().dial(addr, dial_opts); let result = match dial { Ok(out) => { // We only count attempts that the inner transport From 3079aa9fd1334c5f13cc788e093de8ada61d2b08 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:21:12 +0100 Subject: [PATCH 21/97] Port request response --- protocols/request-response/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index fc68bd6cf1f..8a743d731ea 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -79,7 +79,7 @@ pub use handler::ProtocolSupport; use crate::handler::OutboundMessage; use futures::channel::oneshot; use handler::Handler; -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{AddressChange, ConnectionClosed, DialFailure, FromSwarm}, @@ -777,6 +777,7 @@ where peer: PeerId, remote_address: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { let mut handler = Handler::new( self.inbound_protocols.clone(), From 596bb06eb9cb26fd4d7a370124e9978862918e29 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:27:02 +0100 Subject: [PATCH 22/97] Port websocket --- transports/websocket/src/framed.rs | 34 ++++++++++++------------------ transports/websocket/src/lib.rs | 11 ++++------ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/transports/websocket/src/framed.rs b/transports/websocket/src/framed.rs index 3593e1eaff2..73747295170 100644 --- a/transports/websocket/src/framed.rs +++ b/transports/websocket/src/framed.rs @@ -23,9 +23,8 @@ use either::Either; use futures::{future::BoxFuture, prelude::*, ready, stream::BoxStream}; use futures_rustls::{client, rustls, server}; use libp2p_core::{ - connection::Endpoint, multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, Transport, }; use parking_lot::Mutex; @@ -149,15 +148,12 @@ where self.transport.lock().remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.do_dial(addr, Endpoint::Dialer) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - self.do_dial(addr, Endpoint::Listener) + self.do_dial(addr, dial_opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { @@ -263,7 +259,7 @@ where fn do_dial( &mut self, addr: Multiaddr, - role_override: Endpoint, + dial_opts: DialOpts, ) -> Result<::Dial, TransportError<::Error>> { let mut addr = match parse_ws_dial_addr(addr) { Ok(addr) => addr, @@ -282,8 +278,7 @@ where let future = async move { loop { - match Self::dial_once(transport.clone(), addr, tls_config.clone(), role_override) - .await + match Self::dial_once(transport.clone(), addr, tls_config.clone(), dial_opts).await { Ok(Either::Left(redirect)) => { if remaining_redirects == 0 { @@ -307,18 +302,17 @@ where transport: Arc>, addr: WsAddress, tls_config: tls::Config, - role_override: Endpoint, + dial_opts: DialOpts, ) -> Result>, Error> { tracing::trace!(address=?addr, "Dialing websocket address"); - let dial = match role_override { - Endpoint::Dialer => transport.lock().dial(addr.tcp_addr), - Endpoint::Listener => transport.lock().dial_as_listener(addr.tcp_addr), - } - .map_err(|e| match e { - TransportError::MultiaddrNotSupported(a) => Error::InvalidMultiaddr(a), - TransportError::Other(e) => Error::Transport(e), - })?; + let dial = transport + .lock() + .dial(addr.tcp_addr, dial_opts) + .map_err(|e| match e { + TransportError::MultiaddrNotSupported(a) => Error::InvalidMultiaddr(a), + TransportError::Other(e) => Error::Transport(e), + })?; let stream = dial.map_err(Error::Transport).await?; tracing::trace!(port=%addr.host_port, "TCP connection established"); diff --git a/transports/websocket/src/lib.rs b/transports/websocket/src/lib.rs index e0b3d09ca25..3ead3184a5a 100644 --- a/transports/websocket/src/lib.rs +++ b/transports/websocket/src/lib.rs @@ -33,7 +33,7 @@ use futures::{future::BoxFuture, prelude::*, ready}; use libp2p_core::{ connection::ConnectedPoint, multiaddr::Multiaddr, - transport::{map::MapFuture, ListenerId, TransportError, TransportEvent}, + transport::{map::MapFuture, DialOpts, ListenerId, TransportError, TransportEvent}, Transport, }; use rw_stream_sink::RwStreamSink; @@ -202,15 +202,12 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.transport.dial(addr) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { - self.transport.dial_as_listener(addr) + self.transport.dial(addr, opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { From 37cd309ed41e78f7a808c5751ed1564ac875fc56 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:36:51 +0100 Subject: [PATCH 23/97] Port allow block list and connection limits --- misc/allow-block-list/src/lib.rs | 2 ++ misc/connection-limits/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/allow-block-list/src/lib.rs b/misc/allow-block-list/src/lib.rs index c1d31433db1..c877ab09c9b 100644 --- a/misc/allow-block-list/src/lib.rs +++ b/misc/allow-block-list/src/lib.rs @@ -61,6 +61,7 @@ //! # } //! ``` +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -225,6 +226,7 @@ where peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { self.state.enforce(&peer)?; diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index dbe68a8ad11..99008deb36b 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr, transport::PortUse}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, DialFailure, ListenFailure}, @@ -278,6 +278,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { self.pending_outbound_connections.remove(&connection_id); From 9eea564819ce634d9ae339f9287baa4357029883 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:10:08 +0100 Subject: [PATCH 24/97] Address more concerns --- misc/connection-limits/src/lib.rs | 2 +- protocols/autonatv2/src/client/behaviour.rs | 373 ++++++++++-------- protocols/autonatv2/src/client/handler.rs | 1 - .../autonatv2/src/client/handler/dial_back.rs | 35 +- .../src/client/handler/dial_request.rs | 115 +++--- protocols/autonatv2/src/protocol.rs | 8 + protocols/autonatv2/src/server/behaviour.rs | 5 +- .../src/server/handler/dial_request.rs | 24 +- protocols/autonatv2/tests/autonatv2.rs | 70 ++-- 9 files changed, 342 insertions(+), 291 deletions(-) diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index 99008deb36b..e31f50b83cb 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr, transport::PortUse}; +use libp2p_core::{transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, DialFailure, ListenFailure}, diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index b9bb3f0eeb4..65a35c57add 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -14,35 +14,26 @@ use libp2p_swarm::{ ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; -use rand::{seq::SliceRandom, Rng}; -use rand_core::{OsRng, RngCore}; +use rand::prelude::*; +use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use std::sync::Arc; -use crate::{client::handler::dial_request::InternalError, Nonce}; +use crate::client::handler::dial_request::InternalError; use crate::{global_only::IpExt, protocol::DialRequest}; use super::handler::{ dial_back, - dial_request::{self}, + dial_request::{self, InternalStatusUpdate}, TestEnd, }; #[derive(Debug, Clone, Copy)] pub struct Config { - pub(crate) test_server_count: usize, pub(crate) max_addrs_count: usize, pub(crate) recheck_interval: Duration, } impl Config { - pub fn with_test_server_count(self, test_server_count: usize) -> Self { - Self { - test_server_count, - ..self - } - } - pub fn with_max_addrs_count(self, max_addrs_count: usize) -> Self { Self { max_addrs_count, @@ -61,19 +52,17 @@ impl Config { impl Default for Config { fn default() -> Self { Self { - test_server_count: 3, max_addrs_count: 10, recheck_interval: Duration::from_secs(5), } } } + pub struct Behaviour where R: RngCore + 'static, { - local_peers: HashSet, - pending_nonces: HashSet, - known_servers: Vec, + pending_nonces: HashMap, rng: R, config: Config, pending_events: VecDeque< @@ -82,10 +71,10 @@ where <::ConnectionHandler as ConnectionHandler>::FromBehaviour, >, >, - address_candidates: HashMap, + address_candidates: HashMap, already_tested: HashSet, - peers_to_handlers: HashMap, next_tick: Delay, + peer_info: HashMap, } impl NetworkBehaviour for Behaviour @@ -99,12 +88,19 @@ where fn handle_established_inbound_connection( &mut self, connection_id: ConnectionId, - _peer: PeerId, + peer_id: PeerId, _local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result<::ConnectionHandler, ConnectionDenied> { if addr_is_local(remote_addr) { - self.local_peers.insert(connection_id); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: false, + is_local: true, + }) + .is_local = true; } Ok(Either::Right(dial_back::Handler::new())) } @@ -112,33 +108,49 @@ where fn handle_established_outbound_connection( &mut self, connection_id: ConnectionId, - peer: PeerId, + peer_id: PeerId, addr: &Multiaddr, _role_override: Endpoint, _port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { if addr_is_local(addr) { - self.local_peers.insert(connection_id); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: false, + is_local: true, + }) + .is_local = true; } - Ok(Either::Left(dial_request::Handler::new(peer))) + Ok(Either::Left(dial_request::Handler::new(peer_id))) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - self.inject_address_candiate(addr.clone()) + self.address_candidates + .entry(addr.clone()) + .or_default() + .score += 1; } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - self.address_candidates.remove(addr); + if let Some(info) = self.address_candidates.get_mut(addr) { + info.is_tested = true; + } } FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, .. }) => { - self.peers_to_handlers - .entry(peer_id) - .or_insert(connection_id); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: false, + is_local: false, + }); } FromSwarm::ConnectionClosed(ConnectionClosed { peer_id, @@ -149,10 +161,9 @@ where } FromSwarm::DialFailure(DialFailure { peer_id: Some(peer_id), - error, connection_id, + .. }) => { - tracing::trace!("dialing {peer_id:?} failed: {error:?}"); self.handle_no_connection(peer_id, connection_id); } _ => {} @@ -165,61 +176,92 @@ where connection_id: ConnectionId, event: ::ToBehaviour, ) { - if matches!(event, Either::Left(_)) { - self.peers_to_handlers - .entry(peer_id) - .or_insert(connection_id); - } match event { Either::Right(nonce) => { - if self.pending_nonces.remove(&nonce) { + if let Some(status) = self.pending_nonces.get_mut(&nonce) { + *status = NonceStatus::Received; tracing::trace!("Received pending nonce from {peer_id:?}"); } else { tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); } } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { - self.inject_kown_server(peer_id); + self.peer_info + .values_mut() + .filter(|info| info.peer_id == peer_id) + .for_each(|info| { + info.supports_autonat = true; + }); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: true, + is_local: false, + }) + .supports_autonat = true; } - Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { - dial_request: DialRequest { nonce, .. }, - reachable_addr, - }))) => { - if self.pending_nonces.remove(&nonce) { - tracing::debug!( - "server reported reachbility, but didn't actually reached this node." - ); - return; + Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { + tested_addr, + bytes_sent: data_amount, + server, + result, + server_no_support, + })) => { + if server_no_support { + self.peer_info + .values_mut() + .filter(|info| info.peer_id == peer_id) + .for_each(|info| { + info.supports_autonat = false; + }); } - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); - } - Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { - match err.internal.as_ref() { - dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } - | dial_request::InternalError::UnableToConnectOnSelectedAddress { - addr: Some(addr), - } => { - self.pending_events - .push_back(ToSwarm::ExternalAddrExpired(addr.clone())); - } - dial_request::InternalError::InternalServer - | dial_request::InternalError::DataRequestTooLarge { .. } - | dial_request::InternalError::DataRequestTooSmall { .. } - | dial_request::InternalError::InvalidResponse - | dial_request::InternalError::ServerRejectedDialRequest - | dial_request::InternalError::InvalidReferencedAddress { .. } - | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { - self.handle_no_connection(peer_id, connection_id); - } - _ => { - tracing::debug!("Test failed: {:?}", err); + match result { + Ok(TestEnd { + dial_request: DialRequest { nonce, .. }, + ref reachable_addr, + }) => { + if !matches!(self.pending_nonces.get(&nonce), Some(NonceStatus::Received)) { + tracing::debug!( + "server reported reachbility, but didn't actually reached this node." + ); + } else { + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); + } } + Err(ref err) => match &err.internal { + dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } + | dial_request::InternalError::UnableToConnectOnSelectedAddress { + addr: Some(addr), + } => { + if let Some(peer_info) = self.address_candidates.get_mut(addr) { + peer_info.is_tested = true; + } + tracing::debug!(addr = %addr, "Was unable to connect to the server on the selected address.") + } + dial_request::InternalError::InternalServer + | dial_request::InternalError::DataRequestTooLarge { .. } + | dial_request::InternalError::DataRequestTooSmall { .. } + | dial_request::InternalError::InvalidResponse + | dial_request::InternalError::ServerRejectedDialRequest + | dial_request::InternalError::InvalidReferencedAddress { .. } + | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { + self.handle_no_connection(peer_id, connection_id); + } + _ => { + tracing::debug!("Test failed: {:?}", err); + } + }, } + let event = crate::client::Event { + tested_addr, + bytes_sent: data_amount, + server: server.unwrap_or(peer_id), + result: result.map(|_| ()), + }; + self.pending_events.push_back(ToSwarm::GenerateEvent(event)); } - Either::Left(dial_request::ToBehaviour::StatusUpdate(update)) => self - .pending_events - .push_back(ToSwarm::GenerateEvent(update)), } } @@ -247,40 +289,32 @@ where { pub fn new(rng: R, config: Config) -> Self { Self { - local_peers: HashSet::new(), - pending_nonces: HashSet::new(), - known_servers: Vec::new(), + pending_nonces: HashMap::new(), rng, next_tick: Delay::new(config.recheck_interval), config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), - peers_to_handlers: HashMap::new(), already_tested: HashSet::new(), + peer_info: HashMap::new(), } } - /// Injects a known server into the behaviour. It's mostly useful if you are not using identify - /// or for testing purposes. - pub fn inject_kown_server(&mut self, peer: PeerId) { - if !self.known_servers.contains(&peer) { - self.known_servers.push(peer); - } - } - - /// Inject a new address candidate into the behaviour. - pub fn inject_address_candiate(&mut self, addr: Multiaddr) { - *self.address_candidates.entry(addr).or_default() += 1; - } - /// Inject an immediate test for all pending address candidates. - pub fn inject_address_candiate_test(&mut self) { - if self.known_servers.is_empty() || self.address_candidates.is_empty() { + fn inject_address_candiate_test(&mut self) { + if self.peer_info.values().all(|info| !info.supports_autonat) { + return; + } + if self.address_candidates.is_empty() { + return; + } + if self.address_candidates.values().all(|info| info.is_tested) { return; } let mut entries = self .address_candidates .iter() + .filter(|(_, info)| !info.is_tested) .filter(|(addr, _)| !self.already_tested.contains(addr)) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); @@ -294,52 +328,68 @@ where .map(|(addr, _)| addr) .take(self.config.max_addrs_count) .cloned() - .collect::>(); - self.already_tested.extend(addrs.iter().cloned()); - let peers = if self.known_servers.len() < self.config.test_server_count { - self.known_servers.clone() - } else { - self.known_servers - .choose_multiple(&mut self.rng, self.config.test_server_count) - .copied() - .collect() - }; - for peer in peers { - let mut remaining_entries = entries.clone(); - let mut addrs = Vec::with_capacity(self.config.max_addrs_count); - while !remaining_entries.is_empty() && addrs.len() < self.config.max_addrs_count { - let addr = remaining_entries - .choose_weighted(&mut self.rng, |item| item.1) - .unwrap() - .0 - .clone(); - remaining_entries.retain(|(a, _)| a != &addr); - addrs.push(addr); - } - let nonce = self.rng.gen(); - let req = DialRequest { nonce, addrs }; - self.submit_req_for_peer(peer, req, nonce); + .collect(); + if let Some(ConnectionInfo { peer_id, .. }) = self + .peer_info + .values() + .filter(|e| e.supports_autonat) + .choose(&mut self.rng) + { + self.submit_req_for_peer(*peer_id, addrs); } self.next_tick.reset(self.config.recheck_interval); } - fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest, nonce: Nonce) { - self.pending_nonces.insert(nonce); - if let Some(conn_id) = self.peers_to_handlers.get(&peer) { + fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { + let nonce = self.rng.gen(); + let req = DialRequest { nonce, addrs }; + self.pending_nonces.insert(nonce, NonceStatus::Pending); + if let Some(conn_id) = self + .peer_info + .iter() + .filter(|(_, info)| info.supports_autonat) + .find(|(_, info)| info.peer_id == peer) + .map(|(id, _)| *id) + { self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, - handler: NotifyHandler::One(*conn_id), + handler: NotifyHandler::One(conn_id), event: Either::Left(req), }); } } fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { - if matches!(self.peers_to_handlers.get(&peer_id), Some(conn_id) if *conn_id == connection_id) - { - self.peers_to_handlers.remove(&peer_id); + let removeable_conn_ids = self + .peer_info + .iter() + .filter(|(conn_id, info)| info.peer_id == peer_id && **conn_id == connection_id) + .map(|(id, _)| *id) + .collect::>(); + for conn_id in removeable_conn_ids { + self.peer_info.remove(&conn_id); + } + let known_servers_n = self + .peer_info + .values() + .filter(|info| info.supports_autonat) + .count(); + let changed_n = self + .peer_info + .values_mut() + .filter(|info| info.supports_autonat) + .filter(|info| info.peer_id == peer_id) + .map(|info| info.supports_autonat = false) + .count(); + if known_servers_n != changed_n { + tracing::trace!(server = %peer_id, "Removing potential Autonat server due to dial failure"); + } + } + + pub fn validate_addr(&mut self, addr: &Multiaddr) { + if let Some(info) = self.address_candidates.get_mut(addr) { + info.is_tested = true; } - self.known_servers.retain(|p| p != &peer_id); } } @@ -350,36 +400,12 @@ impl Default for Behaviour { } pub struct Error { - pub(crate) internal: Arc, -} - -impl Error { - pub(crate) fn duplicate(&self) -> Self { - Self { - internal: Arc::clone(&self.internal), - } - } + pub(crate) internal: InternalError, } impl From for Error { - fn from(value: InternalError) -> Self { - Self { - internal: Arc::new(value), - } - } -} - -impl From> for Error { - fn from(value: Arc) -> Self { - Self { internal: value } - } -} - -impl From<&Arc> for Error { - fn from(value: &Arc) -> Self { - Self { - internal: Arc::clone(value), - } + fn from(internal: InternalError) -> Self { + Self { internal } } } @@ -403,7 +429,7 @@ pub struct Event { /// The amount of data that was sent to the server. /// Is 0 if it wasn't necessary to send any data. /// Otherwise it's a number between 30.000 and 100.000. - pub data_amount: usize, + pub bytes_sent: usize, /// The peer id of the server that was selected for testing. pub server: PeerId, /// The result of the test. If the test was successful, this is `Ok(())`. @@ -413,12 +439,45 @@ pub struct Event { fn addr_is_local(addr: &Multiaddr) -> bool { addr.iter().any(|c| match c { - Protocol::Dns(addr) - | Protocol::Dns4(addr) - | Protocol::Dns6(addr) - | Protocol::Dnsaddr(addr) => addr.ends_with(".local"), Protocol::Ip4(ip) => !IpExt::is_global(&ip), Protocol::Ip6(ip) => !IpExt::is_global(&ip), _ => false, }) } + +enum NonceStatus { + Pending, + Received, +} + +struct ConnectionInfo { + peer_id: PeerId, + supports_autonat: bool, + is_local: bool, +} + +#[derive(Copy, Clone, Default)] +struct AddressInfo { + score: usize, + is_tested: bool, +} + +impl PartialOrd for AddressInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.score.cmp(&other.score)) + } +} + +impl PartialEq for AddressInfo { + fn eq(&self, other: &Self) -> bool { + self.score == other.score + } +} + +impl Ord for AddressInfo { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.score.cmp(&other.score) + } +} + +impl Eq for AddressInfo {} diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index b30a10d8aae..bb6c9636e2c 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -2,4 +2,3 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; pub(crate) use dial_request::TestEnd; - diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 61d21fcf7ce..2e5eea3038b 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -4,7 +4,6 @@ use std::{ time::Duration, }; -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -13,7 +12,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::{protocol::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; pub struct Handler { inbound: FuturesSet>, @@ -45,16 +44,20 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { - match result { - Ok(Ok(nonce)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) - } - Ok(Err(err)) => tracing::debug!("Dial back handler failed with: {err:?}"), - Err(err) => tracing::debug!("Dial back handler timed out with: {err:?}"), + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(Ok(nonce))) => { + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) + } + Poll::Ready(Ok(Err(err))) => { + tracing::debug!("Dial back handler failed with: {err:?}"); + Poll::Pending + } + Poll::Ready(Err(err)) => { + tracing::debug!("Dial back handler timed out with: {err:?}"); + Poll::Pending } + Poll::Pending => Poll::Pending, } - Poll::Pending } fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} @@ -72,7 +75,11 @@ impl ConnectionHandler for Handler { ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { protocol, .. }) => { - if self.inbound.try_push(perform_dial_back(protocol)).is_err() { + if self + .inbound + .try_push(protocol::recv_dial_back(protocol)) + .is_err() + { tracing::warn!("Dial back request dropped, too many requests in flight"); } } @@ -83,9 +90,3 @@ impl ConnectionHandler for Handler { } } } - -async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { - let DialBack { nonce } = DialBack::read_from(&mut stream).await?; - stream.close().await?; - Ok(nonce) -} diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index bf709a8f0eb..6efa1d8d08b 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,7 +1,4 @@ -use futures::{ - channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, SinkExt, StreamExt, -}; +use futures::{channel::oneshot, AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -19,17 +16,14 @@ use libp2p_swarm::{ }; use std::{ collections::VecDeque, - convert::identity, io, iter::{once, repeat}, - sync::Arc, task::{Context, Poll}, time::Duration, }; use crate::client::behaviour::Error; use crate::{ - client::behaviour::Event, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, @@ -62,6 +56,15 @@ pub(crate) enum InternalError { FailureDuringDialBack { addr: Option }, } +#[derive(Debug)] +pub struct InternalStatusUpdate { + pub(crate) tested_addr: Option, + pub(crate) bytes_sent: usize, + pub(crate) server: Option, + pub result: Result, + pub(crate) server_no_support: bool, +} + #[derive(Debug)] pub struct TestEnd { pub(crate) dial_request: DialRequest, @@ -70,8 +73,7 @@ pub struct TestEnd { #[derive(Debug)] pub enum ToBehaviour { - TestCompleted(Result), - StatusUpdate(Event), + TestCompleted(InternalStatusUpdate), PeerHasServerSupport, } @@ -83,7 +85,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet, queued_streams: VecDeque< oneshot::Sender< Result< @@ -92,20 +94,15 @@ pub struct Handler { >, >, >, - status_update_rx: mpsc::Receiver, - status_update_tx: mpsc::Sender, server: PeerId, } impl Handler { pub(crate) fn new(server: PeerId) -> Self { - let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), - status_update_tx, - status_update_rx, server, } } @@ -119,12 +116,7 @@ impl Handler { }); if self .outbound - .try_push(start_stream_handle( - self.server, - req, - rx, - self.status_update_tx.clone(), - )) + .try_push(start_stream_handle(self.server, req, rx)) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -154,18 +146,20 @@ impl ConnectionHandler for Handler { return Poll::Ready(event); } if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { + let status_update = match m { + Ok(ok) => ok, + Err(_) => InternalStatusUpdate { + tested_addr: None, + bytes_sent: 0, + server: None, + result: Err(Error { + internal: InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)), + }), + server_no_support: false, + }, + }; return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestCompleted( - m.map_err(|_| InternalError::Io(io::Error::from(io::ErrorKind::TimedOut))) - .map_err(Into::into) - .and_then(identity) - .map_err(Into::into), - ), - )); - } - if let Poll::Ready(Some(status_update)) = self.status_update_rx.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::StatusUpdate(status_update), + ToBehaviour::TestCompleted(status_update), )); } Poll::Pending @@ -232,49 +226,56 @@ async fn start_stream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, - mut status_update_tx: mpsc::Sender, -) -> Result { - let substream = match stream_recv.await { - Ok(Ok(substream)) => substream, - Ok(Err(StreamUpgradeError::Io(io))) => return Err(InternalError::from(io).into()), +) -> InternalStatusUpdate { + let mut server_no_support = false; + let substream_result = match stream_recv.await { + Ok(Ok(substream)) => Ok(substream), + Ok(Err(StreamUpgradeError::Io(io))) => Err(InternalError::from(io).into()), Ok(Err(StreamUpgradeError::Timeout)) => { - return Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) + Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) } - Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => { - return Err( - InternalError::Io(io::Error::new(io::ErrorKind::Other, upgrade_error)).into(), + Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => void::unreachable(upgrade_error), + Ok(Err(StreamUpgradeError::NegotiationFailed)) => { + server_no_support = true; + Err( + InternalError::Io(io::Error::new(io::ErrorKind::Other, "negotiation failed")) + .into(), ) } - Ok(Err(StreamUpgradeError::NegotiationFailed)) => { - return Err(InternalError::Io(io::Error::new( - io::ErrorKind::Other, - "negotiation failed", - )) - .into()) + Err(_) => Err(InternalError::InternalServer.into()), + }; + let substream = match substream_result { + Ok(substream) => substream, + Err(err) => { + let status_update = InternalStatusUpdate { + tested_addr: None, + bytes_sent: 0, + server: Some(server), + result: Err(err), + server_no_support, + }; + return status_update; } - Err(_) => return Err(InternalError::InternalServer.into()), }; let mut data_amount = 0; let mut checked_addr_idx = None; let addrs = dial_request.addrs.clone(); assert_ne!(addrs, vec![]); - let res = handle_stream( + let result = handle_stream( dial_request, substream, &mut data_amount, &mut checked_addr_idx, ) .await - .map_err(Arc::new) .map_err(crate::client::behaviour::Error::from); - let status_update = Event { + InternalStatusUpdate { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), - data_amount, - server, - result: res.as_ref().map(|_| ()).map_err(|e| e.duplicate()), - }; - let _ = status_update_tx.send(status_update).await; - res + bytes_sent: data_amount, + server: Some(server), + result, + server_no_support, + } } async fn handle_stream( diff --git a/protocols/autonatv2/src/protocol.rs b/protocols/autonatv2/src/protocol.rs index a12e70bb7c3..b83660cbc65 100644 --- a/protocols/autonatv2/src/protocol.rs +++ b/protocols/autonatv2/src/protocol.rs @@ -272,6 +272,14 @@ pub(crate) async fn dial_back(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) stream.close().await } +pub(crate) async fn recv_dial_back( + mut stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result { + let DialBack { nonce } = DialBack::read_from(&mut stream).await?; + stream.close().await?; + Ok(nonce) +} + const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 0a7bee427df..f1027b43ca4 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -14,7 +14,6 @@ use libp2p_swarm::{ }; use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; -use std::sync::Arc; use crate::server::handler::{ dial_back, @@ -156,11 +155,11 @@ pub struct StatusUpdate { pub all_addrs: Vec, /// The address that was eventually tested. /// This is `None` if the client send and unexpected message. - pub tested_addr: Option, + pub tested_addr: Multiaddr, /// The peer id of the client that submitted addresses for testing. pub client: PeerId, /// The amount of data that was requested by the server and was transmitted. pub data_amount: usize, /// The result of the test. - pub result: Result<(), Arc>, + pub result: Result<(), io::Error>, } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index e04a3d8fc9b..3553ffb1dfd 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -1,6 +1,5 @@ use std::{ io, - sync::Arc, task::{Context, Poll}, time::Duration, }; @@ -52,7 +51,7 @@ pub struct Handler { dial_back_cmd_receiver: mpsc::Receiver, status_update_sender: mpsc::Sender, status_update_receiver: mpsc::Receiver, - inbound: FuturesSet>>, + inbound: FuturesSet>, rng: R, } @@ -82,7 +81,7 @@ where { type FromBehaviour = (); - type ToBehaviour = Either>, StatusUpdate>; + type ToBehaviour = Either, StatusUpdate>; type InboundProtocol = ReadyUpgrade; @@ -110,7 +109,7 @@ where } Poll::Ready(Err(e)) => { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( - Arc::new(io::Error::new(io::ErrorKind::TimedOut, e)), + io::Error::new(io::ErrorKind::TimedOut, e), )))); } Poll::Ready(Ok(Ok(_))) => {} @@ -318,11 +317,11 @@ async fn start_handle_request( dial_back_cmd_sender: mpsc::Sender, mut status_update_sender: mpsc::Sender, rng: impl RngCore, -) -> Result<(), Arc> { +) -> io::Result<()> { let mut all_addrs = Vec::new(); let mut tested_addrs = None; let mut data_amount = 0; - let res = handle_request( + let result = handle_request( stream, observed_multiaddr, dial_back_cmd_sender, @@ -331,15 +330,18 @@ async fn start_handle_request( &mut tested_addrs, &mut data_amount, ) - .await - .map_err(Arc::new); + .await; + if tested_addrs.is_none() { + tracing::warn!("client violated the protocol"); + return Err(io::Error::from(io::ErrorKind::InvalidData)); + } let status_update = StatusUpdate { all_addrs, - tested_addr: tested_addrs, + tested_addr: tested_addrs.unwrap(), client, data_amount, - result: res.as_ref().map_err(Arc::clone).map(|_| ()), + result, }; let _ = status_update_sender.send(status_update).await; - res + Ok(()) } diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index 3024c801660..63e7f5f90a0 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -76,7 +76,7 @@ async fn confirm_successful() { }) .await; - assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); + assert_eq!(tested_addr, bob_external_addrs.get(0).cloned().unwrap()); assert_eq!(data_amount, 0); assert_eq!(client, cor_client_peer); assert_eq!(&all_addrs[..], &bob_external_addrs[..]); @@ -108,7 +108,7 @@ async fn confirm_successful() { let client::Event { tested_addr, - data_amount, + bytes_sent, server, result, } = bob @@ -124,7 +124,7 @@ async fn confirm_successful() { }) .await; assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); - assert_eq!(data_amount, 0); + assert_eq!(bytes_sent, 0); assert_eq!(server, cor_server_peer); assert!(result.is_ok(), "Result is {result:?}"); }; @@ -189,7 +189,7 @@ async fn dial_back_to_unsupported_protocol() { .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { all_addrs, - tested_addr: Some(tested_addr), + tested_addr, client, data_amount, result: Ok(()), @@ -203,30 +203,23 @@ async fn dial_back_to_unsupported_protocol() { }) .await; - tokio::select! { - _ = bob_done_rx => { - return data_amount; - } - _ = alice.loop_on_next() => { - unreachable!(); - } - } + let handler = tokio::spawn(async move { + alice.loop_on_next().await; + }); + let _ = bob_done_rx.await; + handler.abort(); + data_amount }; let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::ExternalAddrExpired { address } if address == bob_test_addr => Some(()), - _ => None, - }) - .await; let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), - data_amount, + bytes_sent, server, result: Err(_), - })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(data_amount), + })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(bytes_sent), _ => None, }) .await; @@ -298,7 +291,7 @@ async fn dial_back_to_non_libp2p() { .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { all_addrs, - tested_addr: Some(tested_addr), + tested_addr, client, data_amount, result: Ok(()), @@ -314,27 +307,23 @@ async fn dial_back_to_non_libp2p() { data_amount }; let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::ExternalAddrExpired { address } if address == bob_addr => Some(()), - _ => None, - }) - .await; let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), - data_amount, + bytes_sent, server, result: Err(_), - })) if tested_addr == bob_addr && server == alice_peer_id => Some(data_amount), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), _ => None, }) .await; data_amount }; - let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_data_amount, bob_data_amount); + let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_bytes_sent, bob_bytes_sent); + bob.behaviour_mut().autonat.validate_addr(&addr); } } @@ -393,7 +382,7 @@ async fn dial_back_to_not_supporting() { .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { all_addrs, - tested_addr: Some(tested_addr), + tested_addr, client, data_amount, result: Ok(()), @@ -417,32 +406,25 @@ async fn dial_back_to_not_supporting() { }; let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::ExternalAddrExpired { address } if address == bob_unreachable_address => { - Some(()) - } - _ => None, - }) - .await; - let data_amount = bob + let bytes_sent = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), - data_amount, + bytes_sent, server, result: Err(_), })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { - Some(data_amount) + Some(bytes_sent) } _ => None, }) .await; bob_done_tx.send(()).unwrap(); - data_amount + bytes_sent }; - let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_data_amount, bob_data_amount); + let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_bytes_sent, bob_bytes_sent); handler.abort(); } @@ -463,7 +445,7 @@ async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { autonat: libp2p_autonatv2::client::Behaviour::new( OsRng, - Config::default().with_recheck_interval(Duration::from_nanos(0)), + Config::default().with_recheck_interval(Duration::from_millis(100)), ), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), From 9a852099deddb341203e685585281e94f5c5202d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:28:08 +0100 Subject: [PATCH 25/97] Move autonat down --- protocols/autonat/src/lib.rs | 42 +------------------ protocols/autonat/src/v1.rs | 41 ++++++++++++++++++ protocols/autonat/src/{ => v1}/behaviour.rs | 17 ++++++-- .../src/{ => v1}/behaviour/as_client.rs | 2 +- .../src/{ => v1}/behaviour/as_server.rs | 1 + .../autonat/src/{ => v1}/generated/mod.rs | 0 .../src/{ => v1}/generated/structs.proto | 0 .../autonat/src/{ => v1}/generated/structs.rs | 0 protocols/autonat/src/{ => v1}/protocol.rs | 2 +- protocols/autonat/tests/test_client.rs | 2 +- protocols/autonat/tests/test_server.rs | 4 +- 11 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 protocols/autonat/src/v1.rs rename protocols/autonat/src/{ => v1}/behaviour.rs (98%) rename protocols/autonat/src/{ => v1}/behaviour/as_client.rs (99%) rename protocols/autonat/src/{ => v1}/behaviour/as_server.rs (99%) rename protocols/autonat/src/{ => v1}/generated/mod.rs (100%) rename protocols/autonat/src/{ => v1}/generated/structs.proto (100%) rename protocols/autonat/src/{ => v1}/generated/structs.rs (100%) rename protocols/autonat/src/{ => v1}/protocol.rs (99%) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 10c87b1e984..a3a6d96c3f5 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1,41 +1 @@ -// Copyright 2021 Protocol Labs. -// -// 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 [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. - -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - -mod behaviour; -mod protocol; - -pub use self::{ - behaviour::{ - Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, - OutboundProbeError, OutboundProbeEvent, ProbeId, - }, - protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, -}; -pub use libp2p_request_response::{InboundFailure, OutboundFailure}; - -mod proto { - #![allow(unreachable_pub)] - include!("generated/mod.rs"); - pub(crate) use self::structs::{mod_Message::*, Message}; -} +pub mod v1; diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs new file mode 100644 index 00000000000..9974d7af59a --- /dev/null +++ b/protocols/autonat/src/v1.rs @@ -0,0 +1,41 @@ +// Copyright 2021 Protocol Labs. +// +// 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 [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. + +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod behaviour; +mod protocol; + +pub use self::{ + behaviour::{ + Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, + OutboundProbeError, OutboundProbeEvent, ProbeId, + }, + protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, +}; +pub use libp2p_request_response::{InboundFailure, OutboundFailure}; + +mod proto { + #![allow(unreachable_pub)] + include!("v1/generated/mod.rs"); + pub(crate) use self::structs::{mod_Message::*, Message}; +} diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/v1/behaviour.rs similarity index 98% rename from protocols/autonat/src/behaviour.rs rename to protocols/autonat/src/v1/behaviour.rs index e95163ab23f..b60fcbb635c 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/v1/behaviour.rs @@ -21,14 +21,15 @@ mod as_client; mod as_server; -use crate::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; -use crate::DEFAULT_PROTOCOL_NAME; +use crate::v1::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; +use crate::v1::DEFAULT_PROTOCOL_NAME; use as_client::AsClient; pub use as_client::{OutboundProbeError, OutboundProbeEvent}; use as_server::AsServer; pub use as_server::{InboundProbeError, InboundProbeEvent}; use futures_timer::Delay; use instant::Instant; +use libp2p_core::transport::PortUse; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_request_response::{ @@ -337,6 +338,7 @@ impl Behaviour { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + port_use: _, } => { if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { self.pending_actions @@ -346,6 +348,7 @@ impl Behaviour { ConnectedPoint::Dialer { address: _, role_override: Endpoint::Listener, + port_use: _, } => { // Outgoing connection was dialed as a listener. In other words outgoing connection // was dialed as part of a hole punch. `libp2p-autonat` never attempts to hole @@ -511,9 +514,15 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/protocols/autonat/src/behaviour/as_client.rs b/protocols/autonat/src/v1/behaviour/as_client.rs similarity index 99% rename from protocols/autonat/src/behaviour/as_client.rs rename to protocols/autonat/src/v1/behaviour/as_client.rs index 668f3b93719..867a5bf171d 100644 --- a/protocols/autonat/src/behaviour/as_client.rs +++ b/protocols/autonat/src/v1/behaviour/as_client.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::ResponseError; +use crate::v1::ResponseError; use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, NatStatus, diff --git a/protocols/autonat/src/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs similarity index 99% rename from protocols/autonat/src/behaviour/as_server.rs rename to protocols/autonat/src/v1/behaviour/as_server.rs index 878fd713dda..4e3cfc77891 100644 --- a/protocols/autonat/src/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -135,6 +135,7 @@ impl<'a> HandleInnerEvent for AsServer<'a> { NonZeroU8::new(1).expect("1 > 0"), ) .addresses(addrs) + .allocate_new_port() .build(), }, ]) diff --git a/protocols/autonat/src/generated/mod.rs b/protocols/autonat/src/v1/generated/mod.rs similarity index 100% rename from protocols/autonat/src/generated/mod.rs rename to protocols/autonat/src/v1/generated/mod.rs diff --git a/protocols/autonat/src/generated/structs.proto b/protocols/autonat/src/v1/generated/structs.proto similarity index 100% rename from protocols/autonat/src/generated/structs.proto rename to protocols/autonat/src/v1/generated/structs.proto diff --git a/protocols/autonat/src/generated/structs.rs b/protocols/autonat/src/v1/generated/structs.rs similarity index 100% rename from protocols/autonat/src/generated/structs.rs rename to protocols/autonat/src/v1/generated/structs.rs diff --git a/protocols/autonat/src/protocol.rs b/protocols/autonat/src/v1/protocol.rs similarity index 99% rename from protocols/autonat/src/protocol.rs rename to protocols/autonat/src/v1/protocol.rs index c1862058400..bee29cae44c 100644 --- a/protocols/autonat/src/protocol.rs +++ b/protocols/autonat/src/v1/protocol.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::proto; +use crate::v1::proto; use async_trait::async_trait; use asynchronous_codec::{FramedRead, FramedWrite}; use futures::io::{AsyncRead, AsyncWrite}; diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 7509d3ef425..8eb7939fbdc 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use async_std::task::JoinHandle; -use libp2p_autonat::{ +use libp2p_autonat::v1::{ Behaviour, Config, Event, NatStatus, OutboundProbeError, OutboundProbeEvent, ResponseError, }; use libp2p_core::Multiaddr; diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index b0610ef59a4..f7e77ce75a7 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_autonat::{ +use libp2p_autonat::v1::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, }; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; @@ -92,6 +92,7 @@ async fn test_dial_back() { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + .. }, num_established, concurrent_dial_errors, @@ -300,6 +301,7 @@ async fn test_dial_multiple_addr() { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + .. }, concurrent_dial_errors, .. From 4399bab570af52a76e3427ffd5c211e0053729d8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Dec 2023 20:56:51 +0100 Subject: [PATCH 26/97] Move autonatv2 to autonat --- Cargo.lock | 41 +++++-------------- Cargo.toml | 5 +-- examples/autonatv2/Cargo.toml | 12 ++++++ examples/autonatv2/src/main.rs | 3 ++ protocols/autonat/Cargo.toml | 31 ++++++++++---- protocols/autonat/src/lib.rs | 10 +++++ .../src/lib.rs => autonat/src/v2.rs} | 0 .../src => autonat/src/v2}/client.rs | 0 .../src/v2}/client/behaviour.rs | 6 +-- .../src => autonat/src/v2}/client/handler.rs | 0 .../src/v2}/client/handler/dial_back.rs | 2 +- .../src/v2}/client/handler/dial_request.rs | 6 +-- .../src => autonat/src/v2}/generated/mod.rs | 0 .../src/v2}/generated/structs.proto | 0 .../src/v2}/generated/structs.rs | 0 .../src => autonat/src/v2}/global_only.rs | 0 .../src => autonat/src/v2}/protocol.rs | 6 +-- .../src => autonat/src/v2}/server.rs | 0 .../src/v2}/server/behaviour.rs | 4 +- .../src => autonat/src/v2}/server/handler.rs | 0 .../src/v2}/server/handler/dial_back.rs | 2 +- .../src/v2}/server/handler/dial_request.rs | 2 +- .../{autonatv2 => autonat}/tests/autonatv2.rs | 12 +++--- protocols/autonatv2/Cargo.toml | 38 ----------------- 24 files changed, 81 insertions(+), 99 deletions(-) create mode 100644 examples/autonatv2/Cargo.toml create mode 100644 examples/autonatv2/src/main.rs rename protocols/{autonatv2/src/lib.rs => autonat/src/v2.rs} (100%) rename protocols/{autonatv2/src => autonat/src/v2}/client.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/client/behaviour.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/client/handler.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/client/handler/dial_back.rs (97%) rename protocols/{autonatv2/src => autonat/src/v2}/client/handler/dial_request.rs (99%) rename protocols/{autonatv2/src => autonat/src/v2}/generated/mod.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/generated/structs.proto (100%) rename protocols/{autonatv2/src => autonat/src/v2}/generated/structs.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/global_only.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/protocol.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/server.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/server/behaviour.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/server/handler.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/server/handler/dial_back.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/server/handler/dial_request.rs (99%) rename protocols/{autonatv2 => autonat}/tests/autonatv2.rs (98%) delete mode 100644 protocols/autonatv2/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 0d25c80c7d0..c1ec0acdf05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,6 +475,10 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "autonatv2" +version = "0.1.0" + [[package]] name = "axum" version = "0.6.20" @@ -2629,30 +2633,9 @@ dependencies = [ [[package]] name = "libp2p-autonat" -version = "0.12.0" +version = "0.13.0" dependencies = [ "async-std", - "async-trait", - "asynchronous-codec", - "futures", - "futures-timer", - "instant", - "libp2p-core", - "libp2p-identity", - "libp2p-request-response", - "libp2p-swarm", - "libp2p-swarm-test", - "quick-protobuf", - "quick-protobuf-codec", - "rand 0.8.5", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "libp2p-autonatv2" -version = "0.1.0" -dependencies = [ "async-trait", "asynchronous-codec", "bytes", @@ -2660,21 +2643,21 @@ dependencies = [ "futures", "futures-bounded", "futures-timer", + "instant", "libp2p-core", "libp2p-identify", "libp2p-identity", + "libp2p-request-response", "libp2p-swarm", "libp2p-swarm-test", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", "rand_core 0.6.4", - "static_assertions", "thiserror", "tokio", "tracing", "tracing-subscriber", - "unsigned-varint 0.8.0", "void", ] @@ -5792,18 +5775,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", @@ -6268,8 +6251,6 @@ checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" dependencies = [ "asynchronous-codec", "bytes", - "futures-io", - "futures-util", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 630a134d975..0ceed838ed6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ members = [ "muxers/test-harness", "muxers/yamux", "protocols/autonat", - "protocols/autonatv2", "protocols/dcutr", "protocols/floodsub", "protocols/gossipsub", @@ -63,7 +62,7 @@ members = [ "transports/websocket", "transports/webtransport-websys", "transports/websocket-websys", - "wasm-tests/webtransport-tests", + "wasm-tests/webtransport-tests", "examples/autonatv2", ] resolver = "2" @@ -75,7 +74,7 @@ asynchronous-codec = { version = "0.7.0" } futures-bounded = { version = "0.2.3", path = "misc/futures-bounded" } libp2p = { version = "0.53.2", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } -libp2p-autonat = { version = "0.12.0", path = "protocols/autonat" } +libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } libp2p-core = { version = "0.41.2", path = "core" } libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml new file mode 100644 index 00000000000..ccefc57c1f7 --- /dev/null +++ b/examples/autonatv2/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "autonatv2" +version = "0.1.0" +edition = "2021" +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lints] +workspace = true diff --git a/examples/autonatv2/src/main.rs b/examples/autonatv2/src/main.rs new file mode 100644 index 00000000000..e7a11a969c0 --- /dev/null +++ b/examples/autonatv2/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index a1ecae7ccab..b20fb4c596c 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -3,32 +3,50 @@ name = "libp2p-autonat" edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" -version = "0.12.0" +version = "0.13.0" authors = ["David Craven ", "Elena Frank "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] + [dependencies] async-trait = "0.1" +asynchronous-codec = { workspace = true } +bytes = { version = "1", optional = true } +either = { version = "1.9.0", optional = true } futures = "0.3" +futures-bounded = { workspace = true, optional = true } futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } -libp2p-swarm = { workspace = true } -libp2p-request-response = { workspace = true } libp2p-identity = { workspace = true } +libp2p-request-response = { workspace = true, optional = true } +libp2p-swarm = { workspace = true } quick-protobuf = "0.8" +quick-protobuf-codec = { workspace = true } rand = "0.8" +rand_core = { version = "0.6", optional = true } +thiserror = { version = "1.0.52", optional = true } tracing = "0.1.37" -quick-protobuf-codec = { workspace = true } -asynchronous-codec = { workspace = true } +void = { version = "1", optional = true } [dev-dependencies] +tokio = { version = "1", features = ["macros", "rt", "sync"]} async-std = { version = "1.10", features = ["attributes"] } libp2p-swarm-test = { path = "../../swarm-test" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } +libp2p-identify = { workspace = true } +libp2p-swarm = { workspace = true, features = ["macros"]} + +[features] +default = ["v1", "v2"] +v1 = ["dep:libp2p-request-response"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] + +[lints] +workspace = true # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling @@ -36,6 +54,3 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } all-features = true rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "docsrs"] - -[lints] -workspace = true diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index a3a6d96c3f5..8a06bea6605 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1 +1,11 @@ +#[cfg(feature = "v1")] pub mod v1; + +#[cfg(feature = "v2")] +pub mod v2; + +#[cfg(feature = "v1")] +#[deprecated(since = "0.13.0", note = "Please use `v1` module instead.")] +pub use v1::*; + + diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonat/src/v2.rs similarity index 100% rename from protocols/autonatv2/src/lib.rs rename to protocols/autonat/src/v2.rs diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonat/src/v2/client.rs similarity index 100% rename from protocols/autonatv2/src/client.rs rename to protocols/autonat/src/v2/client.rs diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs similarity index 98% rename from protocols/autonatv2/src/client/behaviour.rs rename to protocols/autonat/src/v2/client/behaviour.rs index 65a35c57add..657bee24e86 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -18,8 +18,8 @@ use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::client::handler::dial_request::InternalError; -use crate::{global_only::IpExt, protocol::DialRequest}; +use crate::v2::client::handler::dial_request::InternalError; +use crate::v2::{global_only::IpExt, protocol::DialRequest}; use super::handler::{ dial_back, @@ -254,7 +254,7 @@ where } }, } - let event = crate::client::Event { + let event = crate::v2::client::Event { tested_addr, bytes_sent: data_amount, server: server.unwrap_or(peer_id), diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs similarity index 100% rename from protocols/autonatv2/src/client/handler.rs rename to protocols/autonat/src/v2/client/handler.rs diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs similarity index 97% rename from protocols/autonatv2/src/client/handler/dial_back.rs rename to protocols/autonat/src/v2/client/handler/dial_back.rs index 2e5eea3038b..492cc7729a2 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -12,7 +12,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; pub struct Handler { inbound: FuturesSet>, diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs similarity index 99% rename from protocols/autonatv2/src/client/handler/dial_request.rs rename to protocols/autonat/src/v2/client/handler/dial_request.rs index 6efa1d8d08b..a34c76003a4 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -22,8 +22,8 @@ use std::{ time::Duration, }; -use crate::client::behaviour::Error; -use crate::{ +use crate::v2::{ + client::behaviour::Error, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, @@ -268,7 +268,7 @@ async fn start_stream_handle( &mut checked_addr_idx, ) .await - .map_err(crate::client::behaviour::Error::from); + .map_err(crate::v2::client::behaviour::Error::from); InternalStatusUpdate { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), bytes_sent: data_amount, diff --git a/protocols/autonatv2/src/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs similarity index 100% rename from protocols/autonatv2/src/generated/mod.rs rename to protocols/autonat/src/v2/generated/mod.rs diff --git a/protocols/autonatv2/src/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto similarity index 100% rename from protocols/autonatv2/src/generated/structs.proto rename to protocols/autonat/src/v2/generated/structs.proto diff --git a/protocols/autonatv2/src/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs similarity index 100% rename from protocols/autonatv2/src/generated/structs.rs rename to protocols/autonat/src/v2/generated/structs.rs diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonat/src/v2/global_only.rs similarity index 100% rename from protocols/autonatv2/src/global_only.rs rename to protocols/autonat/src/v2/global_only.rs diff --git a/protocols/autonatv2/src/protocol.rs b/protocols/autonat/src/v2/protocol.rs similarity index 98% rename from protocols/autonatv2/src/protocol.rs rename to protocols/autonat/src/v2/protocol.rs index b83660cbc65..69f80bf8d10 100644 --- a/protocols/autonatv2/src/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -11,7 +11,7 @@ use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; use rand::Rng; -use crate::{generated::structs as proto, Nonce}; +use crate::v2::{generated::structs as proto, Nonce}; const REQUEST_MAX_SIZE: usize = 4104; pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; @@ -310,10 +310,10 @@ impl DialBack { #[cfg(test)] mod tests { - use crate::generated::structs::{ + use crate::v2::generated::structs::{ mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; - use crate::protocol::{Coder, DialDataResponse, Request}; + use crate::v2::protocol::{Coder, DialDataResponse, Request}; use futures::io::Cursor; use rand::{thread_rng, Rng}; diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonat/src/v2/server.rs similarity index 100% rename from protocols/autonatv2/src/server.rs rename to protocols/autonat/src/v2/server.rs diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs similarity index 98% rename from protocols/autonatv2/src/server/behaviour.rs rename to protocols/autonat/src/v2/server/behaviour.rs index f1027b43ca4..8f84da1e18f 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -4,7 +4,7 @@ use std::{ task::{Context, Poll}, }; -use crate::server::handler::dial_request::DialBackStatus; +use crate::v2::server::handler::dial_request::DialBackStatus; use either::Either; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -15,7 +15,7 @@ use libp2p_swarm::{ use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; -use crate::server::handler::{ +use crate::v2::server::handler::{ dial_back, dial_request::{self, DialBackCommand}, Handler, diff --git a/protocols/autonatv2/src/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs similarity index 100% rename from protocols/autonatv2/src/server/handler.rs rename to protocols/autonat/src/v2/server/handler.rs diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs similarity index 98% rename from protocols/autonatv2/src/server/handler/dial_back.rs rename to protocols/autonat/src/v2/server/handler/dial_back.rs index c04c45c6fe8..f671434eabe 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs similarity index 99% rename from protocols/autonatv2/src/server/handler/dial_request.rs rename to protocols/autonat/src/v2/server/handler/dial_request.rs index 3553ffb1dfd..beb17d38b7f 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -21,7 +21,7 @@ use libp2p_swarm::{ }; use rand_core::RngCore; -use crate::{ +use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::StatusUpdate, diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs similarity index 98% rename from protocols/autonatv2/tests/autonatv2.rs rename to protocols/autonat/tests/autonatv2.rs index 63e7f5f90a0..618ad73f6ec 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -1,5 +1,5 @@ -use libp2p_autonatv2::client::{self, Config}; -use libp2p_autonatv2::server; +use libp2p_autonat::v2::client::{self, Config}; +use libp2p_autonat::v2::server; use libp2p_core::transport::TransportError; use libp2p_core::Multiaddr; use libp2p_swarm::{ @@ -430,7 +430,7 @@ async fn dial_back_to_not_supporting() { async fn new_server() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedServer { - autonat: libp2p_autonatv2::server::Behaviour::default(), + autonat: libp2p_autonat::v2::server::Behaviour::default(), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), identity.public().clone(), @@ -443,7 +443,7 @@ async fn new_server() -> Swarm { async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { - autonat: libp2p_autonatv2::client::Behaviour::new( + autonat: libp2p_autonat::v2::client::Behaviour::new( OsRng, Config::default().with_recheck_interval(Duration::from_millis(100)), ), @@ -459,14 +459,14 @@ async fn new_client() -> Swarm { #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct CombinedServer { - autonat: libp2p_autonatv2::server::Behaviour, + autonat: libp2p_autonat::v2::server::Behaviour, identify: libp2p_identify::Behaviour, } #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct CombinedClient { - autonat: libp2p_autonatv2::client::Behaviour, + autonat: libp2p_autonat::v2::client::Behaviour, identify: libp2p_identify::Behaviour, } diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml deleted file mode 100644 index 4944502c3e1..00000000000 --- a/protocols/autonatv2/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "libp2p-autonatv2" -version = "0.1.0" -edition = "2021" -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -async-trait = "0.1" -quick-protobuf = "0.8" -quick-protobuf-codec = { workspace = true } -asynchronous-codec = "0.7" -libp2p-core = { workspace = true } -rand_core = "0.6" -rand = "0.8" -libp2p-swarm = { workspace = true } -libp2p-identity = { workspace = true } -futures-bounded = { workspace = true } -void = "1.0.2" -either = "1.9.0" -futures = "0.3.29" -thiserror = "1.0.50" -bytes = "1" -static_assertions = "1.1.0" -tracing = "0.1.40" -unsigned-varint = { workspace = true, features = ["futures"] } -futures-timer = "3.0.2" - -[dev-dependencies] -tokio = { version = "1", features = ["macros", "rt", "sync"] } -libp2p-swarm-test = { workspace = true } -libp2p-identify = { workspace = true } -libp2p-swarm = { workspace = true, features = ["macros"] } -tracing-subscriber = { version = "0.3", features = ["env-filter"]} - -[lints] -workspace = true From 663055e416dd5a02298d99462a832e3978605e88 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 12:55:10 +1100 Subject: [PATCH 27/97] Format code --- protocols/autonat/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 8a06bea6605..714f0f5a89f 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -7,5 +7,3 @@ pub mod v2; #[cfg(feature = "v1")] #[deprecated(since = "0.13.0", note = "Please use `v1` module instead.")] pub use v1::*; - - From ba232cbe8a950a2c2375e58fbe79acc920f69022 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 12:55:48 +1100 Subject: [PATCH 28/97] Alphabetically sort members list --- Cargo.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ceed838ed6..47dfd2014df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "core", "examples/autonat", + "examples/autonatv2", "examples/browser-webrtc", "examples/chat", "examples/dcutr", @@ -46,9 +47,9 @@ members = [ "protocols/rendezvous", "protocols/request-response", "protocols/upnp", - "swarm", "swarm-derive", "swarm-test", + "swarm", "transports/dns", "transports/noise", "transports/plaintext", @@ -57,12 +58,12 @@ members = [ "transports/tcp", "transports/tls", "transports/uds", - "transports/webrtc", "transports/webrtc-websys", + "transports/webrtc", + "transports/websocket-websys", "transports/websocket", "transports/webtransport-websys", - "transports/websocket-websys", - "wasm-tests/webtransport-tests", "examples/autonatv2", + "wasm-tests/webtransport-tests", ] resolver = "2" From 22b96b30252584048bc5585e9a0a1fd2b20d2f96 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 12:59:27 +1100 Subject: [PATCH 29/97] Undo changes to global-only transport --- core/src/transport/global_only.rs | 462 ++++++++++++++---------------- 1 file changed, 220 insertions(+), 242 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index a40f99054d0..d975070ea0f 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -17,272 +17,250 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// -mod ip { - use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - trait Ipv4Ext { - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - fn is_reserved(&self) -> bool; - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - fn is_benchmarking(&self) -> bool; - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - fn is_shared(&self) -> bool; - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - fn is_private(&self) -> bool; +use crate::{ + multiaddr::{Multiaddr, Protocol}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, +}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +/// Dropping all dial requests to non-global IP addresses. +#[derive(Debug, Clone, Default)] +pub struct Transport { + inner: T, +} + +/// This module contains an implementation of the `is_global` IPv4 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) +mod ipv4_global { + use std::net::Ipv4Addr; + + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + #[inline] + const fn is_reserved(a: Ipv4Addr) -> bool { + a.octets()[0] & 240 == 240 && !a.is_broadcast() } - impl Ipv4Ext for Ipv4Addr { - #[inline] - fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - #[inline] - fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - #[inline] - fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - #[inline] - fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if (16..=31).contains(&b) => true, - [192, 168, ..] => true, - _ => false, - } - } + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + #[inline] + const fn is_benchmarking(a: Ipv4Addr) -> bool { + a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 } - trait Ipv6Ext { - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - fn is_unicast_link_local(&self) -> bool; - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - fn is_unique_local(&self) -> bool; - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - fn is_documentation(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + #[inline] + const fn is_shared(a: Ipv4Addr) -> bool { + a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) } - impl Ipv6Ext for Ipv6Addr { - #[inline] - fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + #[inline] + const fn is_private(a: Ipv4Addr) -> bool { + match a.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, } + } - #[inline] - fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[must_use] + #[inline] + pub(crate) const fn is_global(a: Ipv4Addr) -> bool { + !(a.octets()[0] == 0 // "This network" + || is_private(a) + || is_shared(a) + || a.is_loopback() + || a.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) + || a.is_documentation() + || is_benchmarking(a) + || is_reserved(a) + || a.is_broadcast()) + } +} - #[inline] - fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } +/// This module contains an implementation of the `is_global` IPv6 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) +mod ipv6_global { + use std::net::Ipv6Addr; + + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + #[inline] + const fn is_unicast_link_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xffc0) == 0xfe80 } - pub(super) trait IpExt { - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. - #[must_use] - fn is_global(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + #[inline] + const fn is_unique_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xfe00) == 0xfc00 } - impl IpExt for Ipv4Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[inline] - fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || Ipv4Ext::is_shared(self) - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || Ipv4Ext::is_benchmarking(self) - || Ipv4Ext::is_reserved(self) - || self.is_broadcast()) - } + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + #[inline] + const fn is_documentation(a: Ipv6Addr) -> bool { + (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) } - impl IpExt for Ipv6Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[inline] - fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + #[inline] + pub(crate) const fn is_global(a: Ipv6Addr) -> bool { + !(a.is_unspecified() + || a.is_loopback() // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) && !( // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) + || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) )) - || Ipv6Ext::is_documentation(self) - || Ipv6Ext::is_unique_local(self) - || Ipv6Ext::is_unicast_link_local(self)) - } + || is_documentation(a) + || is_unique_local(a) + || is_unicast_link_local(a)) } - - impl IpExt for IpAddr { - #[inline] - fn is_global(&self) -> bool { - match self { - Self::V4(v4) => IpExt::is_global(v4), - Self::V6(v6) => IpExt::is_global(v6), - } - } - } -} - -use crate::{ - multiaddr::{Multiaddr, Protocol}, - transport::{DialOpts, ListenerId, TransportError, TransportEvent}, -}; -use ip::IpExt; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; -use tracing::debug; - -/// Dropping all dial requests to non-global IP addresses. -#[derive(Debug, Clone, Default)] -pub struct Transport { - inner: T, } impl Transport { @@ -316,15 +294,15 @@ impl crate::Transport for Transport { ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if !IpExt::is_global(&a) { - debug!("Not dialing non global IP address {:?}.", a); + if !ipv4_global::is_global(a) { + tracing::debug!(ip=%a, "Not dialing non global IP address"); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr, opts) } Some(Protocol::Ip6(a)) => { - if !IpExt::is_global(&a) { - debug!("Not dialing non global IP address {:?}.", a); + if !ipv6_global::is_global(a) { + tracing::debug!(ip=%a, "Not dialing non global IP address"); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr, opts) From a19d7c89ddab093cd8462847433956c46c80e12a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:05:54 +1100 Subject: [PATCH 30/97] Make deprecation warning work --- protocols/autonat/src/lib.rs | 2 +- protocols/autonat/src/v1.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 714f0f5a89f..a6fc66b28d1 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -5,5 +5,5 @@ pub mod v1; pub mod v2; #[cfg(feature = "v1")] -#[deprecated(since = "0.13.0", note = "Please use `v1` module instead.")] +#[allow(deprecated)] pub use v1::*; diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 9974d7af59a..1c493ce731a 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -21,6 +21,7 @@ //! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![deprecated(note = "Please use `v2` module instead.")] mod behaviour; mod protocol; From 46b8e84d1d5c067de8cc42c3f535d74e05a51e1e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:08:04 +1100 Subject: [PATCH 31/97] Avoid diff in v1 --- protocols/autonat/src/v1.rs | 6 +++--- protocols/autonat/src/v1/behaviour.rs | 4 ++-- protocols/autonat/src/v1/behaviour/as_client.rs | 2 +- protocols/autonat/src/v1/protocol.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 1c493ce731a..245748125cf 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -23,8 +23,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![deprecated(note = "Please use `v2` module instead.")] -mod behaviour; -mod protocol; +pub(crate) mod behaviour; +pub(crate) mod protocol; pub use self::{ behaviour::{ @@ -35,7 +35,7 @@ pub use self::{ }; pub use libp2p_request_response::{InboundFailure, OutboundFailure}; -mod proto { +pub(crate) mod proto { #![allow(unreachable_pub)] include!("v1/generated/mod.rs"); pub(crate) use self::structs::{mod_Message::*, Message}; diff --git a/protocols/autonat/src/v1/behaviour.rs b/protocols/autonat/src/v1/behaviour.rs index b60fcbb635c..4104e9688dc 100644 --- a/protocols/autonat/src/v1/behaviour.rs +++ b/protocols/autonat/src/v1/behaviour.rs @@ -21,8 +21,8 @@ mod as_client; mod as_server; -use crate::v1::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; -use crate::v1::DEFAULT_PROTOCOL_NAME; +use crate::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; +use crate::DEFAULT_PROTOCOL_NAME; use as_client::AsClient; pub use as_client::{OutboundProbeError, OutboundProbeEvent}; use as_server::AsServer; diff --git a/protocols/autonat/src/v1/behaviour/as_client.rs b/protocols/autonat/src/v1/behaviour/as_client.rs index 867a5bf171d..668f3b93719 100644 --- a/protocols/autonat/src/v1/behaviour/as_client.rs +++ b/protocols/autonat/src/v1/behaviour/as_client.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::v1::ResponseError; +use crate::ResponseError; use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, NatStatus, diff --git a/protocols/autonat/src/v1/protocol.rs b/protocols/autonat/src/v1/protocol.rs index bee29cae44c..c1862058400 100644 --- a/protocols/autonat/src/v1/protocol.rs +++ b/protocols/autonat/src/v1/protocol.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::v1::proto; +use crate::proto; use async_trait::async_trait; use asynchronous_codec::{FramedRead, FramedWrite}; use futures::io::{AsyncRead, AsyncWrite}; From 037aa997204ddda763f8061680d699cd2434388f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:09:06 +1100 Subject: [PATCH 32/97] Undo behaviour change in v1 --- protocols/autonat/src/v1/behaviour/as_server.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index 4e3cfc77891..878fd713dda 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -135,7 +135,6 @@ impl<'a> HandleInnerEvent for AsServer<'a> { NonZeroU8::new(1).expect("1 > 0"), ) .addresses(addrs) - .allocate_new_port() .build(), }, ]) From 230ad0847a15fd9846e01d049508d3f82bd63b4c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:12:34 +1100 Subject: [PATCH 33/97] Rename client config fields --- protocols/autonat/src/v2/client/behaviour.rs | 25 +++++++++++--------- protocols/autonat/tests/autonatv2.rs | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 657bee24e86..cb68e236944 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -29,21 +29,24 @@ use super::handler::{ #[derive(Debug, Clone, Copy)] pub struct Config { - pub(crate) max_addrs_count: usize, - pub(crate) recheck_interval: Duration, + /// How many candidates we will test at most. + pub(crate) max_candidates: usize, + + /// The interval at which we will attempt to confirm candidates as external addresses. + pub(crate) probe_interval: Duration, } impl Config { - pub fn with_max_addrs_count(self, max_addrs_count: usize) -> Self { + pub fn with_max_candidates(self, max_candidates: usize) -> Self { Self { - max_addrs_count, + max_candidates, ..self } } - pub fn with_recheck_interval(self, recheck_interval: Duration) -> Self { + pub fn with_probe_interval(self, probe_interval: Duration) -> Self { Self { - recheck_interval, + probe_interval, ..self } } @@ -52,8 +55,8 @@ impl Config { impl Default for Config { fn default() -> Self { Self { - max_addrs_count: 10, - recheck_interval: Duration::from_secs(5), + max_candidates: 10, + probe_interval: Duration::from_secs(5), } } } @@ -291,7 +294,7 @@ where Self { pending_nonces: HashMap::new(), rng, - next_tick: Delay::new(config.recheck_interval), + next_tick: Delay::new(config.probe_interval), config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), @@ -326,7 +329,7 @@ where .iter() .rev() .map(|(addr, _)| addr) - .take(self.config.max_addrs_count) + .take(self.config.max_candidates) .cloned() .collect(); if let Some(ConnectionInfo { peer_id, .. }) = self @@ -337,7 +340,7 @@ where { self.submit_req_for_peer(*peer_id, addrs); } - self.next_tick.reset(self.config.recheck_interval); + self.next_tick.reset(self.config.probe_interval); } fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 618ad73f6ec..951610d84b2 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -445,7 +445,7 @@ async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { autonat: libp2p_autonat::v2::client::Behaviour::new( OsRng, - Config::default().with_recheck_interval(Duration::from_millis(100)), + Config::default().with_probe_interval(Duration::from_millis(100)), ), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), From 7cbf4131ecaed3549e4ab2ca0476aff6a68cd99a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:17:03 +1100 Subject: [PATCH 34/97] Streamline creation of `peer_info` state --- protocols/autonat/src/v2/client/behaviour.rs | 39 +++++--------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index cb68e236944..524b6de618f 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -90,42 +90,22 @@ where fn handle_established_inbound_connection( &mut self, - connection_id: ConnectionId, - peer_id: PeerId, - _local_addr: &Multiaddr, - remote_addr: &Multiaddr, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: &Multiaddr, ) -> Result<::ConnectionHandler, ConnectionDenied> { - if addr_is_local(remote_addr) { - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { - peer_id, - supports_autonat: false, - is_local: true, - }) - .is_local = true; - } Ok(Either::Right(dial_back::Handler::new())) } fn handle_established_outbound_connection( &mut self, - connection_id: ConnectionId, + _: ConnectionId, peer_id: PeerId, - addr: &Multiaddr, - _role_override: Endpoint, - _port_use: PortUse, + _: &Multiaddr, + _: Endpoint, + _: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - if addr_is_local(addr) { - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { - peer_id, - supports_autonat: false, - is_local: true, - }) - .is_local = true; - } Ok(Either::Left(dial_request::Handler::new(peer_id))) } @@ -145,6 +125,7 @@ where FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, + endpoint, .. }) => { self.peer_info @@ -152,7 +133,7 @@ where .or_insert(ConnectionInfo { peer_id, supports_autonat: false, - is_local: false, + is_local: addr_is_local(endpoint.get_remote_address()), }); } FromSwarm::ConnectionClosed(ConnectionClosed { From 16b4d7c0bce936c06c6175fcbb16498283d77fb7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:20:36 +1100 Subject: [PATCH 35/97] Rewrite pending nonce handling Through the use of let-else, we can early-exit on the unhappy-path. We also update the log message to remove the "cause" as there can be a multitude of reasons, why we received a nonce that we didn't expect, e.g. it could also be a bug in the client or the server implementation so we don't want to jump to conclusions here. --- protocols/autonat/src/v2/client/behaviour.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 524b6de618f..e6055389eb1 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -162,12 +162,13 @@ where ) { match event { Either::Right(nonce) => { - if let Some(status) = self.pending_nonces.get_mut(&nonce) { - *status = NonceStatus::Received; - tracing::trace!("Received pending nonce from {peer_id:?}"); - } else { - tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); - } + let Some(status) = self.pending_nonces.get_mut(&nonce) else { + tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); + return; + }; + + *status = NonceStatus::Received; + tracing::debug!(%peer_id, %nonce, "Successful dial-back"); } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { self.peer_info From fdc0c2d34de830328f17c2dbc388493fd95bdc69 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:23:22 +1100 Subject: [PATCH 36/97] A server might not support autonat on every connection Fix the event handling to only update the flag for the connection that the event originated from. --- protocols/autonat/src/v2/client/behaviour.rs | 23 +++++--------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e6055389eb1..be0eec26f8a 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -172,18 +172,8 @@ where } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { self.peer_info - .values_mut() - .filter(|info| info.peer_id == peer_id) - .for_each(|info| { - info.supports_autonat = true; - }); - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { - peer_id, - supports_autonat: true, - is_local: false, - }) + .get_mut(&connection_id) + .expect("inconsistent state") .supports_autonat = true; } Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { @@ -195,12 +185,11 @@ where })) => { if server_no_support { self.peer_info - .values_mut() - .filter(|info| info.peer_id == peer_id) - .for_each(|info| { - info.supports_autonat = false; - }); + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; } + match result { Ok(TestEnd { dial_request: DialRequest { nonce, .. }, From a78c265c53d82f69511a9f14336450aac6216ebd Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:26:37 +1100 Subject: [PATCH 37/97] Prefer early-exit over `else` --- protocols/autonat/src/v2/client/behaviour.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index be0eec26f8a..f196b8864f4 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -196,13 +196,16 @@ where ref reachable_addr, }) => { if !matches!(self.pending_nonces.get(&nonce), Some(NonceStatus::Received)) { - tracing::debug!( - "server reported reachbility, but didn't actually reached this node." - ); - } else { - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); + tracing::warn!( + %peer_id, + %nonce, + "Server reported reachbility but we never received a dial-back" + ); + return; } + + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); } Err(ref err) => match &err.internal { dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } From 8f6c27a03c7f75f640a7959983de0fe6153adef1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:27:29 +1100 Subject: [PATCH 38/97] Use shorthand field logging syntax --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index f196b8864f4..2923163424a 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -215,7 +215,7 @@ where if let Some(peer_info) = self.address_candidates.get_mut(addr) { peer_info.is_tested = true; } - tracing::debug!(addr = %addr, "Was unable to connect to the server on the selected address.") + tracing::debug!(%addr, "Was unable to connect to the server on the selected address.") } dial_request::InternalError::InternalServer | dial_request::InternalError::DataRequestTooLarge { .. } From 170270a6fb4912ed310c2bd4ca4d51b41ec6464a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:27:45 +1100 Subject: [PATCH 39/97] Fix variable name --- protocols/autonat/src/v2/client/behaviour.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 2923163424a..4a7de708e14 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -212,8 +212,8 @@ where | dial_request::InternalError::UnableToConnectOnSelectedAddress { addr: Some(addr), } => { - if let Some(peer_info) = self.address_candidates.get_mut(addr) { - peer_info.is_tested = true; + if let Some(address_info) = self.address_candidates.get_mut(addr) { + address_info.is_tested = true; } tracing::debug!(%addr, "Was unable to connect to the server on the selected address.") } From 589cf6cf691347d0d6faa71b0960045c652efe68 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:29:16 +1100 Subject: [PATCH 40/97] Improve log message --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 4a7de708e14..3bf2bf2cc61 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -215,7 +215,7 @@ where if let Some(address_info) = self.address_candidates.get_mut(addr) { address_info.is_tested = true; } - tracing::debug!(%addr, "Was unable to connect to the server on the selected address.") + tracing::debug!(%peer_id, %addr, "Server failed to dial address, candidate is not a public address") } dial_request::InternalError::InternalServer | dial_request::InternalError::DataRequestTooLarge { .. } From feea82f906a2fc99064bdebc14ce33dc54fddce9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:29:37 +1100 Subject: [PATCH 41/97] Log error using `fmt::Display` --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 3bf2bf2cc61..422416fe416 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -227,7 +227,7 @@ where self.handle_no_connection(peer_id, connection_id); } _ => { - tracing::debug!("Test failed: {:?}", err); + tracing::debug!("Test failed: {err}"); } }, } From 8b0a544d2bb698093153b4265c84d9dd6373abc1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:33:29 +1100 Subject: [PATCH 42/97] Remove `server` field from `InternalStatusUpdate` Connections are always scoped to a particular peer and the PeerId is reported with every event sent from the handler to the behaviour. We don't need to include it in the event. --- protocols/autonat/src/v2/client/behaviour.rs | 7 +++---- .../autonat/src/v2/client/handler/dial_request.rs | 12 ++---------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 422416fe416..0886c0715bf 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -101,12 +101,12 @@ where fn handle_established_outbound_connection( &mut self, _: ConnectionId, - peer_id: PeerId, + _: PeerId, _: &Multiaddr, _: Endpoint, _: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Left(dial_request::Handler::new(peer_id))) + Ok(Either::Left(dial_request::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -179,7 +179,6 @@ where Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { tested_addr, bytes_sent: data_amount, - server, result, server_no_support, })) => { @@ -234,7 +233,7 @@ where let event = crate::v2::client::Event { tested_addr, bytes_sent: data_amount, - server: server.unwrap_or(peer_id), + server: peer_id, result: result.map(|_| ()), }; self.pending_events.push_back(ToSwarm::GenerateEvent(event)); diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index a34c76003a4..0850c4a2bb3 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -5,7 +5,6 @@ use libp2p_core::{ Multiaddr, }; -use libp2p_identity::PeerId; use libp2p_swarm::{ handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, @@ -60,7 +59,6 @@ pub(crate) enum InternalError { pub struct InternalStatusUpdate { pub(crate) tested_addr: Option, pub(crate) bytes_sent: usize, - pub(crate) server: Option, pub result: Result, pub(crate) server_no_support: bool, } @@ -94,16 +92,14 @@ pub struct Handler { >, >, >, - server: PeerId, } impl Handler { - pub(crate) fn new(server: PeerId) -> Self { + pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), - server, } } @@ -116,7 +112,7 @@ impl Handler { }); if self .outbound - .try_push(start_stream_handle(self.server, req, rx)) + .try_push(start_stream_handle(req, rx)) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -151,7 +147,6 @@ impl ConnectionHandler for Handler { Err(_) => InternalStatusUpdate { tested_addr: None, bytes_sent: 0, - server: None, result: Err(Error { internal: InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)), }), @@ -218,7 +213,6 @@ impl ConnectionHandler for Handler { } async fn start_stream_handle( - server: PeerId, dial_request: DialRequest, stream_recv: oneshot::Receiver< Result< @@ -250,7 +244,6 @@ async fn start_stream_handle( let status_update = InternalStatusUpdate { tested_addr: None, bytes_sent: 0, - server: Some(server), result: Err(err), server_no_support, }; @@ -272,7 +265,6 @@ async fn start_stream_handle( InternalStatusUpdate { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), bytes_sent: data_amount, - server: Some(server), result, server_no_support, } From 86849e510604978f01d60a54375b86213ec3a6db Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:34:51 +1100 Subject: [PATCH 43/97] Loop instead of repeating code --- protocols/autonat/src/v2/client/behaviour.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 0886c0715bf..d8ca3900bb1 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -246,16 +246,18 @@ where cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - if self.next_tick.poll_unpin(cx).is_ready() { - self.inject_address_candiate_test(); + loop { if let Some(event) = self.pending_events.pop_front() { return Poll::Ready(event); } + + if self.next_tick.poll_unpin(cx).is_ready() { + self.inject_address_candiate_test(); + continue; + } + + return Poll::Pending; } - Poll::Pending } } From 372d88d828900e6b5adf636417fa599a51ea3e20 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:36:03 +1100 Subject: [PATCH 44/97] Remove set that we never write to This is now handled in the `AddressInfo` struct. --- protocols/autonat/src/v2/client/behaviour.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index d8ca3900bb1..81fecd4e7f5 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, VecDeque}, task::{Context, Poll}, time::Duration, }; @@ -75,7 +75,6 @@ where >, >, address_candidates: HashMap, - already_tested: HashSet, next_tick: Delay, peer_info: HashMap, } @@ -273,7 +272,6 @@ where config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), - already_tested: HashSet::new(), peer_info: HashMap::new(), } } @@ -293,7 +291,6 @@ where .address_candidates .iter() .filter(|(_, info)| !info.is_tested) - .filter(|(addr, _)| !self.already_tested.contains(addr)) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); if entries.is_empty() { From a2fcadd30568f5675a60a91b7c19267a6892a4d6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:36:47 +1100 Subject: [PATCH 45/97] Remove superflous tests We filter straight after this for the same conditions. --- protocols/autonat/src/v2/client/behaviour.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 81fecd4e7f5..f89f46d813f 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -281,12 +281,7 @@ where if self.peer_info.values().all(|info| !info.supports_autonat) { return; } - if self.address_candidates.is_empty() { - return; - } - if self.address_candidates.values().all(|info| info.is_tested) { - return; - } + let mut entries = self .address_candidates .iter() From 90d7d077dfe32469c7c8f917e080bf6a6546d295 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:38:40 +1100 Subject: [PATCH 46/97] Handle tick reset next to polling --- protocols/autonat/src/v2/client/behaviour.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index f89f46d813f..4996310e841 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -251,6 +251,8 @@ where } if self.next_tick.poll_unpin(cx).is_ready() { + self.next_tick.reset(self.config.probe_interval); + self.inject_address_candiate_test(); continue; } @@ -307,7 +309,6 @@ where { self.submit_req_for_peer(*peer_id, addrs); } - self.next_tick.reset(self.config.probe_interval); } fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { From 2a6ad947caece2a07bcb26b8d84a3d49ad3a8f63 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:52:22 +1100 Subject: [PATCH 47/97] Rewrite generation of dial requests An AutoNAT server will always only dial a single address. Thus, we are more likely to get a quicker results for all our candidates if we send each candidate to a different server. --- protocols/autonat/src/v2/client/behaviour.rs | 62 ++++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 4996310e841..b0c73c40d54 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -280,54 +280,51 @@ where /// Inject an immediate test for all pending address candidates. fn inject_address_candiate_test(&mut self) { - if self.peer_info.values().all(|info| !info.supports_autonat) { - return; + for addr in self.untested_candidates() { + let Some((conn_id, peer_id)) = self.random_autonat_server() else { + tracing::debug!("Not connected to any AutoNAT servers"); + return; + }; + + let nonce = self.rng.gen(); + self.pending_nonces.insert(nonce, NonceStatus::Pending); + + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(conn_id), + event: Either::Left(DialRequest { + nonce, + addrs: vec![addr], + }), + }); } + } + fn untested_candidates(&self) -> impl Iterator { let mut entries = self .address_candidates .iter() .filter(|(_, info)| !info.is_tested) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); - if entries.is_empty() { - return; - } + entries.sort_unstable_by_key(|(_, count)| *count); - let addrs = entries - .iter() + + entries + .into_iter() .rev() - .map(|(addr, _)| addr) .take(self.config.max_candidates) - .cloned() - .collect(); - if let Some(ConnectionInfo { peer_id, .. }) = self - .peer_info - .values() - .filter(|e| e.supports_autonat) - .choose(&mut self.rng) - { - self.submit_req_for_peer(*peer_id, addrs); - } + .map(|(addr, _)| addr) } - fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { - let nonce = self.rng.gen(); - let req = DialRequest { nonce, addrs }; - self.pending_nonces.insert(nonce, NonceStatus::Pending); - if let Some(conn_id) = self + fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { + let (conn_id, info) = self .peer_info .iter() .filter(|(_, info)| info.supports_autonat) - .find(|(_, info)| info.peer_id == peer) - .map(|(id, _)| *id) - { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: peer, - handler: NotifyHandler::One(conn_id), - event: Either::Left(req), - }); - } + .choose(&mut self.rng)?; + + Some((*conn_id, info.peer_id)) } fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { @@ -421,6 +418,7 @@ enum NonceStatus { Received, } +#[derive(Clone, Copy)] struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, From b2d147f4c658cb2e81564bbe9aeae08240040166 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:57:43 +1100 Subject: [PATCH 48/97] Add docs and additional log statements for no candidates --- protocols/autonat/src/v2/client/behaviour.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index b0c73c40d54..12f5c052af5 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -253,7 +253,7 @@ where if self.next_tick.poll_unpin(cx).is_ready() { self.next_tick.reset(self.config.probe_interval); - self.inject_address_candiate_test(); + self.issue_dial_requests_for_untested_candidates(); continue; } @@ -278,8 +278,11 @@ where } } - /// Inject an immediate test for all pending address candidates. - fn inject_address_candiate_test(&mut self) { + /// Issues dial requests to random AutoNAT servers for the most frequently reported, untested candidates. + /// + /// In the current implementation, we only send a single address to each AutoNAT server. + /// This spreads our candidates out across all servers we are connected to which should give us pretty fast feedback on all of them. + fn issue_dial_requests_for_untested_candidates(&mut self) { for addr in self.untested_candidates() { let Some((conn_id, peer_id)) = self.random_autonat_server() else { tracing::debug!("Not connected to any AutoNAT servers"); @@ -300,6 +303,9 @@ where } } + /// Returns all untested candidates, sorted by the frequency they were reported at. + /// + /// More frequently reported candidates are considered to more likely be external addresses and thus tested first. fn untested_candidates(&self) -> impl Iterator { let mut entries = self .address_candidates @@ -310,6 +316,10 @@ where entries.sort_unstable_by_key(|(_, count)| *count); + if entries.is_empty() { + tracing::debug!("No untested address candidates"); + } + entries .into_iter() .rev() @@ -418,7 +428,6 @@ enum NonceStatus { Received, } -#[derive(Clone, Copy)] struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, From 7e3da61c5a7d063ee23ec8effb991da2c9ef01ae Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:01:51 +1100 Subject: [PATCH 49/97] Add more docs and rename protocol name constants --- protocols/autonat/src/v2.rs | 4 ++-- protocols/autonat/src/v2/client/behaviour.rs | 1 + protocols/autonat/src/v2/client/handler/dial_back.rs | 4 ++-- protocols/autonat/src/v2/client/handler/dial_request.rs | 6 +++--- protocols/autonat/src/v2/server/handler/dial_back.rs | 4 ++-- protocols/autonat/src/v2/server/handler/dial_request.rs | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 0fae928921b..885c8fda4d0 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -6,9 +6,9 @@ mod global_only; pub(crate) mod protocol; pub mod server; -pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = +pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); -pub(crate) const DIAL_BACK_PROTOCOL_NAME: StreamProtocol = +pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-back"); type Nonce = u64; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 12f5c052af5..897184b2c02 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -327,6 +327,7 @@ where .map(|(addr, _)| addr) } + /// Chooses an active connection to one of our peers that reported support for the [`DIAL_REQUEST_PROTOCOL`](crate::v2::DIAL_REQUEST_PROTOCOL) protocol. fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { let (conn_id, info) = self .peer_info diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 492cc7729a2..18bfee792f1 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -12,7 +12,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; pub struct Handler { inbound: FuturesSet>, @@ -35,7 +35,7 @@ impl ConnectionHandler for Handler { type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()) + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()) } fn poll( diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 0850c4a2bb3..7ff57f9fe8c 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -28,7 +28,7 @@ use crate::v2::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, - REQUEST_PROTOCOL_NAME, + DIAL_REQUEST_PROTOCOL, }; #[derive(Debug, thiserror::Error)] @@ -108,7 +108,7 @@ impl Handler { self.queued_streams.push_back(tx); self.queued_events .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()), }); if self .outbound @@ -200,7 +200,7 @@ impl ConnectionHandler for Handler { } }, ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { - if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { + if added.any(|p| p.as_ref() == DIAL_REQUEST_PROTOCOL) { self.queued_events .push_back(ConnectionHandlerEvent::NotifyBehaviour( ToBehaviour::PeerHasServerSupport, diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index f671434eabe..d9a6e61f4be 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -70,7 +70,7 @@ impl ConnectionHandler for Handler { if let Some(cmd) = self.pending_nonce.take() { self.requested_substream_nonce = Some(cmd); return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()), }); } Poll::Pending diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index beb17d38b7f..04e61d54082 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -25,7 +25,7 @@ use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::StatusUpdate, - Nonce, REQUEST_PROTOCOL_NAME, + Nonce, DIAL_REQUEST_PROTOCOL, }; #[derive(Debug, PartialEq)] @@ -92,7 +92,7 @@ where type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()) + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()) } fn poll( From e2fe41dbb1d6589bd09498b4636804c5018cffed Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:48:42 +1100 Subject: [PATCH 50/97] Inline `DialBack` struct --- protocols/autonat/src/v2/protocol.rs | 56 +++++++++++----------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 69f80bf8d10..ee696f3835b 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, io}; use asynchronous_codec::{Framed, FramedRead, FramedWrite}; -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; @@ -266,46 +266,32 @@ impl DialDataRequest { } } -pub(crate) async fn dial_back(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let dial_back = DialBack { nonce }; - dial_back.write_into(&mut stream).await?; - stream.close().await -} +const DIAL_BACK_MAX_SIZE: usize = 10; -pub(crate) async fn recv_dial_back( - mut stream: impl AsyncRead + AsyncWrite + Unpin, -) -> io::Result { - let DialBack { nonce } = DialBack::read_from(&mut stream).await?; - stream.close().await?; - Ok(nonce) -} +pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let msg = proto::DialBack { nonce: Some(nonce) }; + let mut framed = FramedWrite::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); -const DIAL_BACK_MAX_SIZE: usize = 10; + framed + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + framed.close().await?; -pub(crate) struct DialBack { - pub(crate) nonce: Nonce, + Ok(()) } -impl DialBack { - pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { - let proto::DialBack { nonce } = - FramedRead::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) - .next() - .await - .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - let nonce = ok_or_invalid_data!(nonce)?; - Ok(Self { nonce }) - } +pub(crate) async fn recv_dial_back( + stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result { + let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); + let proto::DialBack { nonce } = framed + .next() + .await + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; + let nonce = ok_or_invalid_data!(nonce)?; - async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = proto::DialBack { - nonce: Some(self.nonce), - }; - FramedWrite::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) - } + Ok(nonce) } #[cfg(test)] From 1e1aeca9031a8579450da73c5311e7dddc1b61d8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:58:53 +1100 Subject: [PATCH 51/97] Simplify handling of closed connection --- protocols/autonat/src/v2/client/behaviour.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 897184b2c02..e154b8b3702 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -140,7 +140,14 @@ where connection_id, .. }) => { - self.handle_no_connection(peer_id, connection_id); + let info = self + .peer_info + .remove(&connection_id) + .expect("inconsistent state"); + + if info.supports_autonat { + tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); + } } FromSwarm::DialFailure(DialFailure { peer_id: Some(peer_id), From d5724c13f17b5af70ba3dbf7a4f197e6aaa2cdee Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:59:47 +1100 Subject: [PATCH 52/97] ConnectionIds are unique, meaning we can always just insert --- protocols/autonat/src/v2/client/behaviour.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e154b8b3702..07f2ac00a2c 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -127,13 +127,14 @@ where endpoint, .. }) => { - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { + self.peer_info.insert( + connection_id, + ConnectionInfo { peer_id, supports_autonat: false, is_local: addr_is_local(endpoint.get_remote_address()), - }); + }, + ); } FromSwarm::ConnectionClosed(ConnectionClosed { peer_id, From de91c5553a252e9a97580aa1bc4088106178bfd3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:00:53 +1100 Subject: [PATCH 53/97] Remove handling of `DialFailure` We only ever insert into the `peer_info` map for established connections. We either establish a connection or receive a `DialFailure`. Thus, there will never be any state in `peer_info` for a failed connection so there is nothing to be cleaned up. --- protocols/autonat/src/v2/client/behaviour.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 07f2ac00a2c..e1ef0866b52 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -11,7 +11,7 @@ use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, - ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, + ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; use rand::prelude::*; @@ -150,13 +150,6 @@ where tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); } } - FromSwarm::DialFailure(DialFailure { - peer_id: Some(peer_id), - connection_id, - .. - }) => { - self.handle_no_connection(peer_id, connection_id); - } _ => {} } } From 3719440bdf98101e3f71bf0b2d0a9234c1a15a7d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:04:51 +1100 Subject: [PATCH 54/97] Disable connection for future autonat requests on error --- protocols/autonat/src/v2/client/behaviour.rs | 39 +++++--------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e1ef0866b52..fe8f201c898 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -216,14 +216,20 @@ where } tracing::debug!(%peer_id, %addr, "Server failed to dial address, candidate is not a public address") } - dial_request::InternalError::InternalServer + e @ (dial_request::InternalError::InternalServer | dial_request::InternalError::DataRequestTooLarge { .. } | dial_request::InternalError::DataRequestTooSmall { .. } | dial_request::InternalError::InvalidResponse | dial_request::InternalError::ServerRejectedDialRequest | dial_request::InternalError::InvalidReferencedAddress { .. } - | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { - self.handle_no_connection(peer_id, connection_id); + | dial_request::InternalError::ServerChoseNotToDialAnyAddress) => { + // Disable this server for future requests. + self.peer_info + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; + + tracing::debug!(%peer_id, %connection_id, %e, "Disabling server for future AutoNAT requests"); } _ => { tracing::debug!("Test failed: {err}"); @@ -339,33 +345,6 @@ where Some((*conn_id, info.peer_id)) } - fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { - let removeable_conn_ids = self - .peer_info - .iter() - .filter(|(conn_id, info)| info.peer_id == peer_id && **conn_id == connection_id) - .map(|(id, _)| *id) - .collect::>(); - for conn_id in removeable_conn_ids { - self.peer_info.remove(&conn_id); - } - let known_servers_n = self - .peer_info - .values() - .filter(|info| info.supports_autonat) - .count(); - let changed_n = self - .peer_info - .values_mut() - .filter(|info| info.supports_autonat) - .filter(|info| info.peer_id == peer_id) - .map(|info| info.supports_autonat = false) - .count(); - if known_servers_n != changed_n { - tracing::trace!(server = %peer_id, "Removing potential Autonat server due to dial failure"); - } - } - pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { info.is_tested = true; From 7b6d35473fea20f773e3ff43a26f5540f7e2f41d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:06:39 +1100 Subject: [PATCH 55/97] Remove unnecessary trait implementations --- protocols/autonat/src/v2/client/behaviour.rs | 22 +------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index fe8f201c898..5ca8d029b40 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -321,7 +321,7 @@ where .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); - entries.sort_unstable_by_key(|(_, count)| *count); + entries.sort_unstable_by_key(|(_, info)| info.score); if entries.is_empty() { tracing::debug!("No untested address candidates"); @@ -420,23 +420,3 @@ struct AddressInfo { score: usize, is_tested: bool, } - -impl PartialOrd for AddressInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.score.cmp(&other.score)) - } -} - -impl PartialEq for AddressInfo { - fn eq(&self, other: &Self) -> bool { - self.score == other.score - } -} - -impl Ord for AddressInfo { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.score.cmp(&other.score) - } -} - -impl Eq for AddressInfo {} From 8ce2a01a5e2553f1c9e4ea18e6f91ff0d4ba7b9a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:07:19 +1100 Subject: [PATCH 56/97] Add comment --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 5ca8d029b40..0022d07f643 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -329,7 +329,7 @@ where entries .into_iter() - .rev() + .rev() // `sort_unstable` is ascending .take(self.config.max_candidates) .map(|(addr, _)| addr) } From 42ac03c60058a0c9de5d99067ff99301a16a6e8d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 16:43:13 +1100 Subject: [PATCH 57/97] Rewrite dial-request to always index by nonce The nonce of a probe is essentially like a primary key that is decided by the client. Thus, any event emitted by the handler should be indexed by the provided nonce. We can achieve this by using a `FuturesMap` instead of a `FuturesSet`. This gives us access to the nonce even in the case that the actual protocol times out. With the nonce in place, we had to re-model the event returned to the behaviour. Most importantly. we need to separate the different kinds of errors: - Complete execution of the protocol but address is not reachable - Protocol was aborted mid-way - Server does not support the protocol We can't really do anything if the protocols is aborted so we just represent this case with an `io::Error` that gets logged further up. As a result, this means we can remove the `Option` from the event emitted to the user and _always_ give them a `Multiaddr`. --- protocols/autonat/src/v2/client/behaviour.rs | 158 ++++---- protocols/autonat/src/v2/client/handler.rs | 2 - .../src/v2/client/handler/dial_request.rs | 341 ++++++++---------- protocols/autonat/src/v2/protocol.rs | 55 ++- 4 files changed, 238 insertions(+), 318 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 0022d07f643..7d558f6434a 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -18,14 +18,9 @@ use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::v2::client::handler::dial_request::InternalError; use crate::v2::{global_only::IpExt, protocol::DialRequest}; -use super::handler::{ - dial_back, - dial_request::{self, InternalStatusUpdate}, - TestEnd, -}; +use super::handler::{dial_back, dial_request}; #[derive(Debug, Clone, Copy)] pub struct Config { @@ -118,7 +113,7 @@ where } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { if let Some(info) = self.address_candidates.get_mut(addr) { - info.is_tested = true; + info.status = TestStatus::Tested; } } FromSwarm::ConnectionEstablished(ConnectionEstablished { @@ -160,7 +155,7 @@ where connection_id: ConnectionId, event: ::ToBehaviour, ) { - match event { + let (nonce, outcome) = match event { Either::Right(nonce) => { let Some(status) = self.pending_nonces.get_mut(&nonce) else { tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); @@ -169,82 +164,63 @@ where *status = NonceStatus::Received; tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + return; } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { self.peer_info .get_mut(&connection_id) .expect("inconsistent state") .supports_autonat = true; + return; + } + Either::Left(dial_request::ToBehaviour::TestOutcome { nonce, outcome }) => { + (nonce, outcome) } - Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { - tested_addr, - bytes_sent: data_amount, - result, - server_no_support, - })) => { - if server_no_support { - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = false; + }; + + let nonce_status = self.pending_nonces.remove(&nonce); + + let ((tested_addr, bytes_sent), result) = match outcome { + Ok(address) => { + if nonce_status != Some(NonceStatus::Received) { + tracing::warn!( + %peer_id, + %nonce, + "Server reported reachbility but we never received a dial-back" + ); + return; } - match result { - Ok(TestEnd { - dial_request: DialRequest { nonce, .. }, - ref reachable_addr, - }) => { - if !matches!(self.pending_nonces.get(&nonce), Some(NonceStatus::Received)) { - tracing::warn!( - %peer_id, - %nonce, - "Server reported reachbility but we never received a dial-back" - ); - return; - } - - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); - } - Err(ref err) => match &err.internal { - dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } - | dial_request::InternalError::UnableToConnectOnSelectedAddress { - addr: Some(addr), - } => { - if let Some(address_info) = self.address_candidates.get_mut(addr) { - address_info.is_tested = true; - } - tracing::debug!(%peer_id, %addr, "Server failed to dial address, candidate is not a public address") - } - e @ (dial_request::InternalError::InternalServer - | dial_request::InternalError::DataRequestTooLarge { .. } - | dial_request::InternalError::DataRequestTooSmall { .. } - | dial_request::InternalError::InvalidResponse - | dial_request::InternalError::ServerRejectedDialRequest - | dial_request::InternalError::InvalidReferencedAddress { .. } - | dial_request::InternalError::ServerChoseNotToDialAnyAddress) => { - // Disable this server for future requests. - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = false; - - tracing::debug!(%peer_id, %connection_id, %e, "Disabling server for future AutoNAT requests"); - } - _ => { - tracing::debug!("Test failed: {err}"); - } - }, - } - let event = crate::v2::client::Event { - tested_addr, - bytes_sent: data_amount, - server: peer_id, - result: result.map(|_| ()), - }; - self.pending_events.push_back(ToSwarm::GenerateEvent(event)); + (address, Ok(())) } - } + Err(dial_request::Error::UnsupportedProtocol) => { + self.peer_info + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; + return; + } + Err(dial_request::Error::Io(e)) => { + tracing::debug!( + %peer_id, + %nonce, + "Failed to complete AutoNAT probe: {e}" + ); + return; + } + Err(dial_request::Error::AddressNotReachable { + address, + bytes_sent, + error, + }) => ((address, bytes_sent), Err(error)), + }; + + self.pending_events.push_back(ToSwarm::GenerateEvent(Event { + tested_addr, + bytes_sent, + server: peer_id, + result: result.map_err(|e| Error { inner: e }), + })); } fn poll( @@ -298,6 +274,10 @@ where let nonce = self.rng.gen(); self.pending_nonces.insert(nonce, NonceStatus::Pending); + self.address_candidates + .get_mut(&addr) + .expect("only emit candidates") + .status = TestStatus::Pending; self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id, @@ -317,7 +297,7 @@ where let mut entries = self .address_candidates .iter() - .filter(|(_, info)| !info.is_tested) + .filter(|(_, info)| info.status == TestStatus::Untested) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); @@ -347,7 +327,7 @@ where pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { - info.is_tested = true; + info.status = TestStatus::Tested; } } } @@ -359,32 +339,25 @@ impl Default for Behaviour { } pub struct Error { - pub(crate) internal: InternalError, -} - -impl From for Error { - fn from(internal: InternalError) -> Self { - Self { internal } - } + pub(crate) inner: dial_request::DialBackError, } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(&self.internal, f) + Display::fmt(&self.inner, f) } } impl Debug for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self.internal, f) + Debug::fmt(&self.inner, f) } } #[derive(Debug)] pub struct Event { /// The address that was selected for testing. - /// Is `None` in the case that the server respond with something unexpected. - pub tested_addr: Option, + pub tested_addr: Multiaddr, /// The amount of data that was sent to the server. /// Is 0 if it wasn't necessary to send any data. /// Otherwise it's a number between 30.000 and 100.000. @@ -404,6 +377,7 @@ fn addr_is_local(addr: &Multiaddr) -> bool { }) } +#[derive(Debug, PartialEq)] enum NonceStatus { Pending, Received, @@ -418,5 +392,13 @@ struct ConnectionInfo { #[derive(Copy, Clone, Default)] struct AddressInfo { score: usize, - is_tested: bool, + status: TestStatus, +} + +#[derive(Clone, Copy, Default, PartialEq)] +enum TestStatus { + #[default] + Untested, + Pending, + Tested, } diff --git a/protocols/autonat/src/v2/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs index bb6c9636e2c..e526c2fb44c 100644 --- a/protocols/autonat/src/v2/client/handler.rs +++ b/protocols/autonat/src/v2/client/handler.rs @@ -1,4 +1,2 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; - -pub(crate) use dial_request::TestEnd; diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 7ff57f9fe8c..4b071894215 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -1,5 +1,5 @@ -use futures::{channel::oneshot, AsyncRead, AsyncWrite}; -use futures_bounded::FuturesSet; +use futures::{channel::oneshot, AsyncWrite}; +use futures_bounded::FuturesMap; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, @@ -22,57 +22,49 @@ use std::{ }; use crate::v2::{ - client::behaviour::Error, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{ - Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + Coder, DialDataRequest, DialDataResponse, DialRequest, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, - DIAL_REQUEST_PROTOCOL, + Nonce, DIAL_REQUEST_PROTOCOL, }; -#[derive(Debug, thiserror::Error)] -pub(crate) enum InternalError { - #[error("io error")] - Io(#[from] io::Error), - #[error("invalid referenced address index: {index} (max number of addr: {max})")] - InvalidReferencedAddress { index: usize, max: usize }, - #[error("data request too large: {len} (max: {max})")] - DataRequestTooLarge { len: usize, max: usize }, - #[error("data request too small: {len} (min: {min})")] - DataRequestTooSmall { len: usize, min: usize }, - #[error("server rejected dial request")] - ServerRejectedDialRequest, - #[error("server chose not to dial any provided address")] - ServerChoseNotToDialAnyAddress, - #[error("server ran into an internal error")] - InternalServer, - #[error("server did not respond correctly to dial request")] - InvalidResponse, - #[error("server was unable to connect to address: {addr:?}")] - UnableToConnectOnSelectedAddress { addr: Option }, - #[error("server experienced failure during dial back on address: {addr:?}")] - FailureDuringDialBack { addr: Option }, +#[derive(Debug)] +pub enum ToBehaviour { + TestOutcome { + nonce: Nonce, + outcome: Result<(Multiaddr, usize), Error>, + }, + PeerHasServerSupport, } -#[derive(Debug)] -pub struct InternalStatusUpdate { - pub(crate) tested_addr: Option, - pub(crate) bytes_sent: usize, - pub result: Result, - pub(crate) server_no_support: bool, +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Address is not reachable: {error}")] + AddressNotReachable { + address: Multiaddr, + bytes_sent: usize, + error: DialBackError, + }, + #[error("Peer does not support AutoNAT dial-request protocol")] + UnsupportedProtocol, + #[error("IO error: {0}")] + Io(io::Error), } -#[derive(Debug)] -pub struct TestEnd { - pub(crate) dial_request: DialRequest, - pub(crate) reachable_addr: Multiaddr, +impl From for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } } -#[derive(Debug)] -pub enum ToBehaviour { - TestCompleted(InternalStatusUpdate), - PeerHasServerSupport, +#[derive(thiserror::Error, Debug)] +pub enum DialBackError { + #[error("server failed to establish a connection")] + NoConnection, + #[error("dial back stream failed")] + StreamFailed, } pub struct Handler { @@ -83,7 +75,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet, + outbound: FuturesMap>, queued_streams: VecDeque< oneshot::Sender< Result< @@ -98,7 +90,7 @@ impl Handler { pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(Duration::from_secs(10), 10), + outbound: FuturesMap::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), } } @@ -112,7 +104,7 @@ impl Handler { }); if self .outbound - .try_push(start_stream_handle(req, rx)) + .try_push(req.nonce, start_stream_handle(req, rx)) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -141,22 +133,24 @@ impl ConnectionHandler for Handler { if let Some(event) = self.queued_events.pop_front() { return Poll::Ready(event); } - if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { - let status_update = match m { - Ok(ok) => ok, - Err(_) => InternalStatusUpdate { - tested_addr: None, - bytes_sent: 0, - result: Err(Error { - internal: InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)), - }), - server_no_support: false, - }, - }; - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestCompleted(status_update), - )); + + match self.outbound.poll_unpin(cx) { + Poll::Ready((nonce, Ok(outcome))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestOutcome { nonce, outcome }, + )) + } + Poll::Ready((nonce, Err(_))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestOutcome { + nonce, + outcome: Err(Error::Io(io::ErrorKind::TimedOut.into())), + }, + )); + } + Poll::Pending => {} } + Poll::Pending } @@ -213,177 +207,126 @@ impl ConnectionHandler for Handler { } async fn start_stream_handle( - dial_request: DialRequest, - stream_recv: oneshot::Receiver< - Result< - Stream, - StreamUpgradeError< as OutboundUpgradeSend>::Error>, - >, - >, -) -> InternalStatusUpdate { - let mut server_no_support = false; - let substream_result = match stream_recv.await { - Ok(Ok(substream)) => Ok(substream), - Ok(Err(StreamUpgradeError::Io(io))) => Err(InternalError::from(io).into()), - Ok(Err(StreamUpgradeError::Timeout)) => { - Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) - } - Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => void::unreachable(upgrade_error), - Ok(Err(StreamUpgradeError::NegotiationFailed)) => { - server_no_support = true; - Err( - InternalError::Io(io::Error::new(io::ErrorKind::Other, "negotiation failed")) - .into(), - ) - } - Err(_) => Err(InternalError::InternalServer.into()), - }; - let substream = match substream_result { - Ok(substream) => substream, - Err(err) => { - let status_update = InternalStatusUpdate { - tested_addr: None, - bytes_sent: 0, - result: Err(err), - server_no_support, - }; - return status_update; - } - }; - let mut data_amount = 0; - let mut checked_addr_idx = None; - let addrs = dial_request.addrs.clone(); - assert_ne!(addrs, vec![]); - let result = handle_stream( - dial_request, - substream, - &mut data_amount, - &mut checked_addr_idx, - ) - .await - .map_err(crate::v2::client::behaviour::Error::from); - InternalStatusUpdate { - tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), - bytes_sent: data_amount, - result, - server_no_support, - } -} + req: DialRequest, + stream_recv: oneshot::Receiver>>, +) -> Result<(Multiaddr, usize), Error> { + let stream = stream_recv + .await + .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))? + .map_err(|e| match e { + StreamUpgradeError::NegotiationFailed => Error::UnsupportedProtocol, + StreamUpgradeError::Timeout => Error::Io(io::ErrorKind::TimedOut.into()), + StreamUpgradeError::Apply(v) => void::unreachable(v), + StreamUpgradeError::Io(e) => Error::Io(e), + })?; -async fn handle_stream( - dial_request: DialRequest, - stream: impl AsyncRead + AsyncWrite + Unpin, - data_amount: &mut usize, - checked_addr_idx: &mut Option, -) -> Result { let mut coder = Coder::new(stream); - coder.send(Request::Dial(dial_request.clone())).await?; - match coder.next().await? { + coder.send(req.clone()).await?; + + let (res, bytes_sent) = match coder.next().await? { Response::Data(DialDataRequest { addr_idx, num_bytes, }) => { - if addr_idx >= dial_request.addrs.len() { - return Err(InternalError::InvalidReferencedAddress { - index: addr_idx, - max: dial_request.addrs.len(), - }); - } - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(InternalError::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); + if addr_idx >= req.addrs.len() { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "address index out of bounds", + ))); } - if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(InternalError::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - *checked_addr_idx = Some(addr_idx); - send_aap_data(&mut coder, num_bytes, data_amount).await?; - if let Response::Dial(dial_response) = coder.next().await? { - *checked_addr_idx = Some(dial_response.addr_idx); - coder.close().await?; - test_end_from_dial_response(dial_request, dial_response) - } else { - Err(InternalError::InternalServer) + if num_bytes > DATA_LEN_UPPER_BOUND || num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "requested bytes out of bounds", + ))); } + + send_aap_data(&mut coder, num_bytes).await?; + + let Response::Dial(dial_response) = coder.next().await? else { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "expected message", + ))); + }; + + (dial_response, num_bytes) } - Response::Dial(dial_response) => { - *checked_addr_idx = Some(dial_response.addr_idx); - coder.close().await?; - test_end_from_dial_response(dial_request, dial_response) + Response::Dial(dial_response) => (dial_response, 0), + }; + coder.close().await?; + + match res.status { + ResponseStatus::E_REQUEST_REJECTED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server rejected request", + ))) + } + ResponseStatus::E_DIAL_REFUSED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server refused dial", + ))) + } + ResponseStatus::E_INTERNAL_ERROR => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server encountered internal error", + ))) } + ResponseStatus::OK => {} } -} -fn test_end_from_dial_response( - req: DialRequest, - resp: DialResponse, -) -> Result { - if resp.addr_idx >= req.addrs.len() { - return Err(InternalError::InvalidReferencedAddress { - index: resp.addr_idx, - max: req.addrs.len(), - }); - } - match (resp.status, resp.dial_status) { - (ResponseStatus::E_REQUEST_REJECTED, _) => Err(InternalError::ServerRejectedDialRequest), - (ResponseStatus::E_DIAL_REFUSED, _) => Err(InternalError::ServerChoseNotToDialAnyAddress), - (ResponseStatus::E_INTERNAL_ERROR, _) => Err(InternalError::InternalServer), - (ResponseStatus::OK, DialStatus::UNUSED) => Err(InternalError::InvalidResponse), - (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { - Err(InternalError::UnableToConnectOnSelectedAddress { - addr: req.addrs.get(resp.addr_idx).cloned(), - }) + let tested_address = req + .addrs + .get(res.addr_idx) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "address index out of bounds"))? + .clone(); + + match res.dial_status { + DialStatus::UNUSED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "unexpected message", + ))) } - (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { - Err(InternalError::FailureDuringDialBack { - addr: req.addrs.get(resp.addr_idx).cloned(), + DialStatus::E_DIAL_ERROR => { + return Err(Error::AddressNotReachable { + address: tested_address, + bytes_sent, + error: DialBackError::NoConnection, }) } - (ResponseStatus::OK, DialStatus::OK) => req - .addrs - .get(resp.addr_idx) - .ok_or(InternalError::InvalidReferencedAddress { - index: resp.addr_idx, - max: req.addrs.len(), + DialStatus::E_DIAL_BACK_ERROR => { + return Err(Error::AddressNotReachable { + address: tested_address, + bytes_sent, + error: DialBackError::StreamFailed, }) - .cloned() - .map(|reachable_addr| TestEnd { - dial_request: req, - reachable_addr, - }), + } + DialStatus::OK => {} } + + Ok((tested_address, bytes_sent)) } -async fn send_aap_data( - stream: &mut Coder, - num_bytes: usize, - data_amount: &mut usize, -) -> io::Result<()> +async fn send_aap_data(stream: &mut Coder, num_bytes: usize) -> io::Result<()> where I: AsyncWrite + Unpin, { let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; - for (data_count, req) in repeat(DATA_FIELD_LEN_UPPER_BOUND) + for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) .take(count_full) .chain(once(partial_len)) .filter(|e| *e > 0) .map(|data_count| { - ( - data_count, - Request::Data( - DialDataResponse::new(data_count).expect("data count is unexpectedly too big"), - ), - ) + DialDataResponse::new(data_count).expect("data count is unexpectedly too big") }) { - *data_amount += data_count; stream.send(req).await?; } + Ok(()) } diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index ee696f3835b..b4a1cbab4f6 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -90,6 +90,32 @@ pub(crate) enum Request { Data(DialDataResponse), } +impl From for proto::Message { + fn from(val: DialRequest) -> Self { + let addrs = val.addrs.iter().map(|e| e.to_vec()).collect(); + let nonce = Some(val.nonce); + + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }), + } + } +} + +impl From for proto::Message { + fn from(val: DialDataResponse) -> Self { + debug_assert!( + val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { + data: Some(Cow::Borrowed(&DATA[..val.data_count])), + }), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub struct DialRequest { pub(crate) addrs: Vec, @@ -144,35 +170,6 @@ impl TryFrom for Request { } } -impl Into for Request { - fn into(self) -> proto::Message { - match self { - Request::Dial(DialRequest { addrs, nonce }) => { - let addrs = addrs.iter().map(|e| e.to_vec()).collect(); - let nonce = Some(nonce); - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { - addrs, - nonce, - }), - } - } - Request::Data(DialDataResponse { data_count }) => { - debug_assert!( - data_count <= DATA_FIELD_LEN_UPPER_BOUND, - "data_count too large" - ); - static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(Cow::Borrowed(&DATA[..data_count])), - }), - } - } - } - } -} - #[derive(Debug, Clone)] pub(crate) enum Response { Dial(DialResponse), From 318111384310f5214258584e8499cfed73865fd5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 17:28:36 +1100 Subject: [PATCH 58/97] Reset TestStatus on errors --- protocols/autonat/src/v2/client/behaviour.rs | 89 ++++++++++++++------ 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 7d558f6434a..a7836add87b 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -10,15 +10,14 @@ use futures_timer::Delay; use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, - ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, - NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, + behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, + ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::v2::{global_only::IpExt, protocol::DialRequest}; +use crate::v2::{global_only::IpExt, protocol::DialRequest, Nonce}; use super::handler::{dial_back, dial_request}; @@ -60,7 +59,6 @@ pub struct Behaviour where R: RngCore + 'static, { - pending_nonces: HashMap, rng: R, config: Config, pending_events: VecDeque< @@ -111,11 +109,6 @@ where .or_default() .score += 1; } - FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - if let Some(info) = self.address_candidates.get_mut(addr) { - info.status = TestStatus::Tested; - } - } FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, @@ -157,13 +150,18 @@ where ) { let (nonce, outcome) = match event { Either::Right(nonce) => { - let Some(status) = self.pending_nonces.get_mut(&nonce) else { + let Some((_, info)) = self + .address_candidates + .iter_mut() + .find(|(_, info)| info.is_pending_with_nonce(nonce)) + else { tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); return; }; - *status = NonceStatus::Received; + info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + return; } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { @@ -178,11 +176,14 @@ where } }; - let nonce_status = self.pending_nonces.remove(&nonce); - let ((tested_addr, bytes_sent), result) = match outcome { Ok(address) => { - if nonce_status != Some(NonceStatus::Received) { + let received_dial_back = self + .address_candidates + .iter_mut() + .any(|(_, info)| info.is_received_with_nonce(nonce)); + + if !received_dial_back { tracing::warn!( %peer_id, %nonce, @@ -198,6 +199,9 @@ where .get_mut(&connection_id) .expect("inconsistent state") .supports_autonat = false; + + self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. + return; } Err(dial_request::Error::Io(e)) => { @@ -206,13 +210,20 @@ where %nonce, "Failed to complete AutoNAT probe: {e}" ); + + self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. + return; } Err(dial_request::Error::AddressNotReachable { address, bytes_sent, error, - }) => ((address, bytes_sent), Err(error)), + }) => { + self.reset_status_to(nonce, TestStatus::Failed); + + ((address, bytes_sent), Err(error)) + } }; self.pending_events.push_back(ToSwarm::GenerateEvent(Event { @@ -251,7 +262,6 @@ where { pub fn new(rng: R, config: Config) -> Self { Self { - pending_nonces: HashMap::new(), rng, next_tick: Delay::new(config.probe_interval), config, @@ -273,11 +283,10 @@ where }; let nonce = self.rng.gen(); - self.pending_nonces.insert(nonce, NonceStatus::Pending); self.address_candidates .get_mut(&addr) .expect("only emit candidates") - .status = TestStatus::Pending; + .status = TestStatus::Pending(nonce); self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id, @@ -325,9 +334,22 @@ where Some((*conn_id, info.peer_id)) } + fn reset_status_to(&mut self, nonce: Nonce, new_status: TestStatus) { + let Some((_, info)) = self + .address_candidates + .iter_mut() + .find(|(_, i)| i.is_pending_with_nonce(nonce) || i.is_received_with_nonce(nonce)) + else { + return; + }; + + info.status = new_status; + } + + // FIXME: We don't want test-only APIs in our public API. pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { - info.status = TestStatus::Tested; + info.status = TestStatus::Received(self.rng.next_u64()); } } } @@ -377,12 +399,6 @@ fn addr_is_local(addr: &Multiaddr) -> bool { }) } -#[derive(Debug, PartialEq)] -enum NonceStatus { - Pending, - Received, -} - struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, @@ -395,10 +411,27 @@ struct AddressInfo { status: TestStatus, } +impl AddressInfo { + fn is_pending_with_nonce(&self, nonce: Nonce) -> bool { + match self.status { + TestStatus::Pending(c) => c == nonce, + _ => false, + } + } + + fn is_received_with_nonce(&self, nonce: Nonce) -> bool { + match self.status { + TestStatus::Received(c) => c == nonce, + _ => false, + } + } +} + #[derive(Clone, Copy, Default, PartialEq)] enum TestStatus { #[default] Untested, - Pending, - Tested, + Pending(Nonce), + Failed, + Received(Nonce), } From 4d96c33d9768c78aa65ab3cbbcc32dfcf5646f8b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 14:49:18 +0100 Subject: [PATCH 59/97] Delay and use Result --- Cargo.lock | 13 ++++++++++ protocols/autonat/Cargo.toml | 1 + .../autonat/src/v1/behaviour/as_server.rs | 1 + protocols/autonat/src/v2/client/behaviour.rs | 1 + protocols/autonat/src/v2/protocol.rs | 22 ----------------- protocols/autonat/src/v2/server/behaviour.rs | 2 +- .../src/v2/server/handler/dial_back.rs | 6 ++--- .../src/v2/server/handler/dial_request.rs | 24 ++++++++++--------- protocols/autonat/tests/autonatv2.rs | 11 +++++---- 9 files changed, 40 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1ec0acdf05..5d3de078ee3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,6 +1736,18 @@ dependencies = [ "instant", ] +[[package]] +name = "futures-time" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6404853a6824881fe5f7d662d147dc4e84ecd2259ba0378f272a71dab600758a" +dependencies = [ + "async-channel", + "async-io 1.13.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "futures-timer" version = "3.0.2" @@ -2642,6 +2654,7 @@ dependencies = [ "either", "futures", "futures-bounded", + "futures-time", "futures-timer", "instant", "libp2p-core", diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index b20fb4c596c..3a79215e2cf 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -18,6 +18,7 @@ bytes = { version = "1", optional = true } either = { version = "1.9.0", optional = true } futures = "0.3" futures-bounded = { workspace = true, optional = true } +futures-time = "3" futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index 878fd713dda..4e3cfc77891 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -135,6 +135,7 @@ impl<'a> HandleInnerEvent for AsServer<'a> { NonZeroU8::new(1).expect("1 > 0"), ) .addresses(addrs) + .allocate_new_port() .build(), }, ]) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index a7836add87b..700a567e218 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -347,6 +347,7 @@ where } // FIXME: We don't want test-only APIs in our public API. + #[doc(hidden)] pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { info.status = TestStatus::Received(self.rng.next_u64()); diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index b4a1cbab4f6..99c1541a8e8 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -297,7 +297,6 @@ mod tests { mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; use crate::v2::protocol::{Coder, DialDataResponse, Request}; - use futures::io::Cursor; use rand::{thread_rng, Rng}; @@ -328,25 +327,4 @@ mod tests { let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); } - - #[tokio::test] - async fn write_read_request() { - let mut buf = Cursor::new(Vec::new()); - let mut coder = Coder::new(&mut buf); - let mut all_req = Vec::with_capacity(100); - for _ in 0..100 { - let data_request: Request = Request::Data(DialDataResponse { - data_count: thread_rng().gen_range(0..4000), - }); - all_req.push(data_request.clone()); - coder.send(data_request.clone()).await.unwrap(); - } - let inner = coder.inner.into_inner(); - inner.set_position(0); - let mut coder = Coder::new(inner); - for i in 0..100 { - let read_data_request: Request = coder.next().await.unwrap(); - assert_eq!(read_data_request, all_req[i]); - } - } } diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 8f84da1e18f..a808e44c2fd 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -100,7 +100,7 @@ where if let Some(DialBackCommand { back_channel, .. }) = self.dialing_dial_back.remove(&connection_id) { - let _ = back_channel.send(DialBackStatus::DialErr); + let _ = back_channel.send(Err(DialBackStatus::DialErr)); } } _ => {} diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index d9a6e61f4be..4adefe51c87 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -108,7 +108,7 @@ impl ConnectionHandler for Handler { .. }) => { if let Some(cmd) = self.requested_substream_nonce.take() { - let _ = cmd.back_channel.send(DialBackRes::DialBackErr); + let _ = cmd.back_channel.send(Err(DialBackRes::DialBackErr)); } } _ => {} @@ -124,11 +124,11 @@ async fn perform_dial_back( .. }: DialBackCommand, ) -> io::Result<()> { + futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; let res = dial_back(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) - .map(|_| DialBackRes::Ok) - .unwrap_or_else(|e| e); + .map(|_| ()); back_channel .send(res) .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index 04e61d54082..bc004d4b541 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -34,14 +34,13 @@ pub(crate) enum DialBackStatus { DialErr, /// Failure during dial back DialBackErr, - Ok, } #[derive(Debug)] pub struct DialBackCommand { pub(crate) addr: Multiaddr, pub(crate) nonce: Nonce, - pub(crate) back_channel: oneshot::Sender, + pub(crate) back_channel: oneshot::Sender>, } pub struct Handler { @@ -172,7 +171,10 @@ enum HandleFail { InternalError(usize), RequestRejected, DialRefused, - DialBack { idx: usize, err: DialBackStatus }, + DialBack { + idx: usize, + result: Result<(), DialBackStatus>, + }, } impl From for DialResponse { @@ -193,13 +195,13 @@ impl From for DialResponse { addr_idx: 0, dial_status: DialStatus::UNUSED, }, - HandleFail::DialBack { idx, err } => Self { + HandleFail::DialBack { idx, result } => Self { status: ResponseStatus::OK, addr_idx: idx, - dial_status: match err { - DialBackStatus::DialErr => DialStatus::E_DIAL_ERROR, - DialBackStatus::DialBackErr => DialStatus::E_DIAL_BACK_ERROR, - DialBackStatus::Ok => DialStatus::OK, + dial_status: match result { + Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, + Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, + Ok(()) => DialStatus::OK, }, }, } @@ -267,14 +269,14 @@ where .await .map_err(|_| HandleFail::DialBack { idx, - err: DialBackStatus::DialErr, + result: Err(DialBackStatus::DialErr), })?; let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; - if dial_back != DialBackStatus::Ok { + if let Err(err) = dial_back { return Err(HandleFail::DialBack { idx, - err: dial_back, + result: Err(err), }); } Ok(DialResponse { diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 951610d84b2..2b896f7086d 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -123,7 +123,10 @@ async fn confirm_successful() { _ => None, }) .await; - assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); + assert_eq!( + tested_addr, + alice_bob_external_addrs.get(0).cloned().unwrap() + ); assert_eq!(bytes_sent, 0); assert_eq!(server, cor_server_peer); assert!(result.is_ok(), "Result is {result:?}"); @@ -215,7 +218,7 @@ async fn dial_back_to_unsupported_protocol() { let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr: Some(tested_addr), + tested_addr, bytes_sent, server, result: Err(_), @@ -310,7 +313,7 @@ async fn dial_back_to_non_libp2p() { let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr: Some(tested_addr), + tested_addr, bytes_sent, server, result: Err(_), @@ -409,7 +412,7 @@ async fn dial_back_to_not_supporting() { let bytes_sent = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr: Some(tested_addr), + tested_addr, bytes_sent, server, result: Err(_), From 8a135ea4093e46cd7a48a082bdc3a6940aebeab9 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:08:26 +0100 Subject: [PATCH 60/97] Perform sleep correct --- protocols/autonat/src/v2/server/handler/dial_back.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index 4adefe51c87..f419c9a6fc3 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -124,11 +124,11 @@ async fn perform_dial_back( .. }: DialBackCommand, ) -> io::Result<()> { - futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; let res = dial_back(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| ()); + futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; back_channel .send(res) .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; From fac1196473b7923edc0e2dfa840ebffc2f933cdb Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:12:19 +0100 Subject: [PATCH 61/97] Remove is_local --- protocols/autonat/src/v2.rs | 1 - protocols/autonat/src/v2/client/behaviour.rs | 16 +- protocols/autonat/src/v2/global_only.rs | 247 ------------------- 3 files changed, 3 insertions(+), 261 deletions(-) delete mode 100644 protocols/autonat/src/v2/global_only.rs diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 885c8fda4d0..d95d51b79e9 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -2,7 +2,6 @@ use libp2p_swarm::StreamProtocol; pub mod client; mod generated; -mod global_only; pub(crate) mod protocol; pub mod server; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 700a567e218..baeddc5b1f6 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -7,7 +7,7 @@ use std::{ use either::Either; use futures::FutureExt; use futures_timer::Delay; -use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, @@ -17,7 +17,7 @@ use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::v2::{global_only::IpExt, protocol::DialRequest, Nonce}; +use crate::v2::{protocol::DialRequest, Nonce}; use super::handler::{dial_back, dial_request}; @@ -112,7 +112,7 @@ where FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, - endpoint, + endpoint: _, .. }) => { self.peer_info.insert( @@ -120,7 +120,6 @@ where ConnectionInfo { peer_id, supports_autonat: false, - is_local: addr_is_local(endpoint.get_remote_address()), }, ); } @@ -392,18 +391,9 @@ pub struct Event { pub result: Result<(), Error>, } -fn addr_is_local(addr: &Multiaddr) -> bool { - addr.iter().any(|c| match c { - Protocol::Ip4(ip) => !IpExt::is_global(&ip), - Protocol::Ip6(ip) => !IpExt::is_global(&ip), - _ => false, - }) -} - struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, - is_local: bool, } #[derive(Copy, Clone, Default)] diff --git a/protocols/autonat/src/v2/global_only.rs b/protocols/autonat/src/v2/global_only.rs deleted file mode 100644 index af788639399..00000000000 --- a/protocols/autonat/src/v2/global_only.rs +++ /dev/null @@ -1,247 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -pub(crate) trait Ipv4Ext { - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - fn is_reserved(&self) -> bool; - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - fn is_benchmarking(&self) -> bool; - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - fn is_shared(&self) -> bool; - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - fn is_private(&self) -> bool; -} - -impl Ipv4Ext for Ipv4Addr { - #[inline] - fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - #[inline] - fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - #[inline] - fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - #[inline] - fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if (16..=31).contains(&b) => true, - [192, 168, ..] => true, - _ => false, - } - } -} - -pub(crate) trait Ipv6Ext { - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - fn is_unicast_link_local(&self) -> bool; - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - fn is_unique_local(&self) -> bool; - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - fn is_documentation(&self) -> bool; -} - -impl Ipv6Ext for Ipv6Addr { - #[inline] - fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - #[inline] - fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - #[inline] - fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } -} - -pub(crate) trait IpExt { - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. - #[must_use] - fn is_global(&self) -> bool; -} - -impl IpExt for Ipv4Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[inline] - fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || Ipv4Ext::is_shared(self) - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || Ipv4Ext::is_benchmarking(self) - || Ipv4Ext::is_reserved(self) - || self.is_broadcast()) - } -} - -impl IpExt for Ipv6Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[inline] - fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) - )) - || Ipv6Ext::is_documentation(self) - || Ipv6Ext::is_unique_local(self) - || Ipv6Ext::is_unicast_link_local(self)) - } -} - -impl IpExt for IpAddr { - #[inline] - fn is_global(&self) -> bool { - match self { - Self::V4(v4) => IpExt::is_global(v4), - Self::V6(v6) => IpExt::is_global(v6), - } - } -} From 9ed44d9c762da72ba61d5cae544b9eceea641814 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:16:22 +0100 Subject: [PATCH 62/97] Remove the unnecessary check for confirmation --- protocols/autonat/tests/autonatv2.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 2b896f7086d..94f1bb18e5a 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -84,12 +84,11 @@ async fn confirm_successful() { }; let bob_task = async { - let address_candidate = bob - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; + bob.wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; let incoming_conn_id = bob .wait(|event| match event { SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), @@ -116,10 +115,6 @@ async fn confirm_successful() { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { Some(status_update) } - SwarmEvent::ExternalAddrConfirmed { address } => { - assert_eq!(address, address_candidate); - None - } _ => None, }) .await; @@ -565,7 +560,6 @@ async fn bootstrap() -> (Swarm, Swarm) { bob.wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), - SwarmEvent::ExternalAddrConfirmed { .. } => None, _ => None, }) .await; From a97470706ba4abb7df312e57104a831a3c42f4a2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 17:04:42 +0100 Subject: [PATCH 63/97] Fix deprecation --- protocols/autonat/src/v1.rs | 2 +- protocols/autonat/src/v1/behaviour/as_server.rs | 1 - protocols/autonat/src/v2/protocol.rs | 3 --- protocols/autonat/tests/test_client.rs | 3 ++- protocols/autonat/tests/test_server.rs | 3 ++- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 245748125cf..07b08310871 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -21,7 +21,7 @@ //! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![deprecated(note = "Please use `v2` module instead.")] +#![cfg_attr(not(test), deprecated(note = "Please use `v2` module instead."))] pub(crate) mod behaviour; pub(crate) mod protocol; diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index 4e3cfc77891..e309023bc75 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -17,7 +17,6 @@ // 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 super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, ResponseError, diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 99c1541a8e8..f6776093868 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -296,9 +296,6 @@ mod tests { use crate::v2::generated::structs::{ mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; - use crate::v2::protocol::{Coder, DialDataResponse, Request}; - - use rand::{thread_rng, Rng}; #[test] fn message_correct_max_size() { diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 8eb7939fbdc..2b1460cac85 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -17,9 +17,10 @@ // 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. +#![allow(deprecated)] use async_std::task::JoinHandle; -use libp2p_autonat::v1::{ +use libp2p_autonat::{ Behaviour, Config, Event, NatStatus, OutboundProbeError, OutboundProbeEvent, ResponseError, }; use libp2p_core::Multiaddr; diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index f7e77ce75a7..a01c5901b8e 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -17,8 +17,9 @@ // 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. +#![allow(deprecated)] -use libp2p_autonat::v1::{ +use libp2p_autonat::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, }; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; From 3f2476a7ede1c71114b4cd7cc3557835ffb0ada2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:24:32 +0100 Subject: [PATCH 64/97] Implement first jan suggestions --- protocols/autonat/src/v2/client/behaviour.rs | 3 + .../src/v2/client/handler/dial_request.rs | 2 +- protocols/autonat/src/v2/server.rs | 2 +- protocols/autonat/src/v2/server/behaviour.rs | 35 ++-- protocols/autonat/src/v2/server/handler.rs | 4 +- .../src/v2/server/handler/dial_back.rs | 12 +- .../src/v2/server/handler/dial_request.rs | 170 ++++++++---------- protocols/autonat/tests/autonatv2.rs | 8 +- 8 files changed, 106 insertions(+), 130 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index baeddc5b1f6..e257a4b833d 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -191,6 +191,9 @@ where return; } + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(address.0.clone())); + (address, Ok(())) } Err(dial_request::Error::UnsupportedProtocol) => { diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 4b071894215..16934c478fb 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -186,7 +186,7 @@ impl ConnectionHandler for Handler { }) => match self.queued_streams.pop_front() { Some(stream_tx) => { if stream_tx.send(Ok(protocol)).is_err() { - tracing::warn!("Failed to send stream to dead handler"); + tracing::debug!("Failed to send stream to dead handler"); } } None => { diff --git a/protocols/autonat/src/v2/server.rs b/protocols/autonat/src/v2/server.rs index e864893d73d..25819307784 100644 --- a/protocols/autonat/src/v2/server.rs +++ b/protocols/autonat/src/v2/server.rs @@ -2,4 +2,4 @@ mod behaviour; mod handler; pub use behaviour::Behaviour; -pub use behaviour::StatusUpdate; +pub use behaviour::Event; diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index a808e44c2fd..907faeec823 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -8,11 +8,11 @@ use crate::v2::server::handler::dial_request::DialBackStatus; use either::Either; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ - dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, - NetworkBehaviour, ToSwarm, + dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, + FromSwarm, NetworkBehaviour, ToSwarm, }; -use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; use crate::v2::server::handler::{ @@ -60,7 +60,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = StatusUpdate; + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -84,19 +84,15 @@ where _role_override: Endpoint, _port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok( - if let Some(cmd) = self.dialing_dial_back.remove(&connection_id) { - Either::Left(dial_back::Handler::new(cmd)) - } else { - Either::Left(dial_back::Handler::empty()) - }, - ) + Ok(match self.dialing_dial_back.remove(&connection_id) { + Some(cmd) => Either::Left(Either::Left(dial_back::Handler::new(cmd))), + None => Either::Left(Either::Right(dummy::ConnectionHandler)), + }) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { - FromSwarm::DialFailure(DialFailure { connection_id, .. }) - | FromSwarm::ConnectionClosed(ConnectionClosed { connection_id, .. }) => { + FromSwarm::DialFailure(DialFailure { connection_id, .. }) => { if let Some(DialBackCommand { back_channel, .. }) = self.dialing_dial_back.remove(&connection_id) { @@ -114,11 +110,12 @@ where event: as ConnectionHandler>::ToBehaviour, ) { match event { - Either::Left(Ok(_)) => {} - Either::Left(Err(e)) => { + Either::Left(Either::Left(Ok(_))) => {} + Either::Left(Either::Left(Err(e))) => { tracing::debug!("dial back error: {e:?}"); } - Either::Right(Either::Left(Ok(cmd))) => { + Either::Left(Either::Right(v)) => void::unreachable(v), + Either::Right(Either::Left(cmd)) => { let addr = cmd.addr.clone(); let opts = DialOpts::peer_id(peer_id) .addresses(Vec::from([addr])) @@ -129,9 +126,6 @@ where self.dialing_dial_back.insert(conn_id, cmd); self.pending_events.push_back(ToSwarm::Dial { opts }); } - Either::Right(Either::Left(Err(e))) => { - tracing::warn!("incoming dial request failed: {}", e); - } Either::Right(Either::Right(status_update)) => self .pending_events .push_back(ToSwarm::GenerateEvent(status_update)), @@ -150,11 +144,10 @@ where } #[derive(Debug)] -pub struct StatusUpdate { +pub struct Event { /// All address that were submitted for testing. pub all_addrs: Vec, /// The address that was eventually tested. - /// This is `None` if the client send and unexpected message. pub tested_addr: Multiaddr, /// The peer id of the client that submitted addresses for testing. pub client: PeerId, diff --git a/protocols/autonat/src/v2/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs index 764e1a71f1f..ffdad69c86f 100644 --- a/protocols/autonat/src/v2/server/handler.rs +++ b/protocols/autonat/src/v2/server/handler.rs @@ -1,6 +1,8 @@ use either::Either; +use libp2p_swarm::dummy; pub(crate) mod dial_back; pub(crate) mod dial_request; -pub(crate) type Handler = Either>; +pub(crate) type Handler = + Either, dial_request::Handler>; diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index f419c9a6fc3..2aa87fd1294 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -28,16 +28,10 @@ pub struct Handler { impl Handler { pub(crate) fn new(cmd: DialBackCommand) -> Self { - let mut ret = Self::empty(); - ret.pending_nonce = Some(cmd); - ret - } - - pub(crate) fn empty() -> Self { Self { - pending_nonce: None, + pending_nonce: Some(cmd), requested_substream_nonce: None, - outbound: FuturesSet::new(Duration::from_secs(10000), 2), + outbound: FuturesSet::new(Duration::from_secs(10), 5), } } } @@ -128,6 +122,8 @@ async fn perform_dial_back( .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| ()); + // this exists to prevent a synchronization issue on the client side. Whitout this, the client + // might already receive a futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; back_channel .send(res) diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index bc004d4b541..382df27391f 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -24,7 +24,7 @@ use rand_core::RngCore; use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, - server::behaviour::StatusUpdate, + server::behaviour::Event, Nonce, DIAL_REQUEST_PROTOCOL, }; @@ -48,9 +48,7 @@ pub struct Handler { observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, dial_back_cmd_receiver: mpsc::Receiver, - status_update_sender: mpsc::Sender, - status_update_receiver: mpsc::Receiver, - inbound: FuturesSet>, + inbound: FuturesSet, rng: R, } @@ -60,14 +58,11 @@ where { pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); - let (status_update_sender, status_update_receiver) = mpsc::channel(10); Self { client_id, observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, - status_update_sender, - status_update_receiver, inbound: FuturesSet::new(Duration::from_secs(10), 10), rng, } @@ -78,16 +73,11 @@ impl ConnectionHandler for Handler where R: RngCore + Send + Clone + 'static, { - type FromBehaviour = (); - - type ToBehaviour = Either, StatusUpdate>; - + type FromBehaviour = void::Void; + type ToBehaviour = Either; type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = DeniedUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { @@ -101,28 +91,21 @@ where ConnectionHandlerEvent, > { match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(Err(e))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( - e, - )))); + Poll::Ready(Ok(event)) => { + if let Err(e) = &event.result { + tracing::warn!("inbound request handle failed: {:?}", e); + } + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + event, + ))); } Poll::Ready(Err(e)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( - io::Error::new(io::ErrorKind::TimedOut, e), - )))); + tracing::warn!("inbound request handle timed out {e:?}"); } - Poll::Ready(Ok(Ok(_))) => {} Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Ok( - cmd, - )))); - } - if let Poll::Ready(Some(status_update)) = self.status_update_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( - status_update, - ))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); } Poll::Pending } @@ -144,12 +127,11 @@ where }) => { if self .inbound - .try_push(start_handle_request( + .try_push(handle_request( protocol, self.observed_multiaddr.clone(), self.client_id, self.dial_back_cmd_sender.clone(), - self.status_update_sender.clone(), self.rng.clone(), )) .is_err() @@ -208,6 +190,68 @@ impl From for DialResponse { } } +async fn handle_request( + stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + client: PeerId, + dial_back_cmd_sender: mpsc::Sender, + rng: impl RngCore, +) -> Event { + let mut coder = Coder::new(stream); + let mut all_addrs = Vec::new(); + let mut tested_addr_opt = None; + let mut data_amount = 0; + let response = handle_request_internal( + &mut coder, + observed_multiaddr.clone(), + dial_back_cmd_sender, + rng, + &mut all_addrs, + &mut tested_addr_opt, + &mut data_amount, + ) + .await + .unwrap_or_else(|e| e.into()); + if tested_addr_opt.is_none() { + return Event { + all_addrs, + tested_addr: observed_multiaddr, + client, + data_amount, + result: Err(io::Error::new( + io::ErrorKind::Other, + "client is not conformint to protocol. the tested address is not the observed address", + )), + }; + } + let tested_addr = tested_addr_opt.unwrap(); + if let Err(e) = coder.send(Response::Dial(response)).await { + return Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Err(e), + }; + } + if let Err(e) = coder.close().await { + return Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Err(e), + }; + } + Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Ok(()), + } +} + async fn handle_request_internal( coder: &mut Coder, observed_multiaddr: Multiaddr, @@ -285,65 +329,3 @@ where dial_status: DialStatus::OK, }) } - -async fn handle_request( - stream: impl AsyncRead + AsyncWrite + Unpin, - observed_multiaddr: Multiaddr, - dial_back_cmd_sender: mpsc::Sender, - rng: impl RngCore, - all_addrs: &mut Vec, - tested_addrs: &mut Option, - data_amount: &mut usize, -) -> io::Result<()> { - let mut coder = Coder::new(stream); - let response = handle_request_internal( - &mut coder, - observed_multiaddr, - dial_back_cmd_sender, - rng, - all_addrs, - tested_addrs, - data_amount, - ) - .await - .unwrap_or_else(|e| e.into()); - coder.send(Response::Dial(response)).await?; - coder.close().await?; - Ok(()) -} - -async fn start_handle_request( - stream: impl AsyncRead + AsyncWrite + Unpin, - observed_multiaddr: Multiaddr, - client: PeerId, - dial_back_cmd_sender: mpsc::Sender, - mut status_update_sender: mpsc::Sender, - rng: impl RngCore, -) -> io::Result<()> { - let mut all_addrs = Vec::new(); - let mut tested_addrs = None; - let mut data_amount = 0; - let result = handle_request( - stream, - observed_multiaddr, - dial_back_cmd_sender, - rng, - &mut all_addrs, - &mut tested_addrs, - &mut data_amount, - ) - .await; - if tested_addrs.is_none() { - tracing::warn!("client violated the protocol"); - return Err(io::Error::from(io::ErrorKind::InvalidData)); - } - let status_update = StatusUpdate { - all_addrs, - tested_addr: tested_addrs.unwrap(), - client, - data_amount, - result, - }; - let _ = status_update_sender.send(status_update).await; - Ok(()) -} diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 94f1bb18e5a..150de669362 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -61,7 +61,7 @@ async fn confirm_successful() { }) .await; - let server::StatusUpdate { + let server::Event { all_addrs, tested_addr, client, @@ -185,7 +185,7 @@ async fn dial_back_to_unsupported_protocol() { assert_eq!(outgoing_conn_error.len(), 0); let data_amount = alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, tested_addr, client, @@ -287,7 +287,7 @@ async fn dial_back_to_non_libp2p() { let data_amount = alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, tested_addr, client, @@ -378,7 +378,7 @@ async fn dial_back_to_not_supporting() { let data_amount = alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, tested_addr, client, From 9254faad6a0d41d2baa02f0c7353c5cdf499c9e3 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:30:22 +0100 Subject: [PATCH 65/97] Implement an example and a public tester --- Cargo.lock | 178 ++++++++++++++++-- examples/autonatv2/Cargo.toml | 26 +++ examples/autonatv2/Dockerfile | 20 ++ examples/autonatv2/docker-compose.yml | 16 ++ .../autonatv2/src/bin/autonatv2_client.rs | 111 +++++++++++ .../autonatv2/src/bin/autonatv2_server.rs | 86 +++++++++ examples/autonatv2/src/lib.rs | 0 examples/autonatv2/src/main.rs | 3 - 8 files changed, 423 insertions(+), 17 deletions(-) create mode 100644 examples/autonatv2/Dockerfile create mode 100644 examples/autonatv2/docker-compose.yml create mode 100644 examples/autonatv2/src/bin/autonatv2_client.rs create mode 100644 examples/autonatv2/src/bin/autonatv2_server.rs create mode 100644 examples/autonatv2/src/lib.rs delete mode 100644 examples/autonatv2/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index d739c7c2420..0ffe0f0214f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,6 +478,19 @@ dependencies = [ [[package]] name = "autonatv2" version = "0.1.0" +dependencies = [ + "cfg-if", + "clap", + "libp2p", + "opentelemetry 0.21.0", + "opentelemetry-jaeger", + "opentelemetry_sdk 0.21.2", + "rand 0.8.5", + "tokio", + "tracing", + "tracing-opentelemetry 0.22.0", + "tracing-subscriber", +] [[package]] name = "axum" @@ -904,9 +917,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -914,9 +927,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -2378,6 +2391,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + [[package]] name = "interceptor" version = "0.10.0" @@ -3723,13 +3742,13 @@ dependencies = [ "futures", "hyper 0.14.27", "libp2p", - "opentelemetry", + "opentelemetry 0.20.0", "opentelemetry-otlp", "opentelemetry_api", "prometheus-client", "tokio", "tracing", - "tracing-opentelemetry", + "tracing-opentelemetry 0.21.0", "tracing-subscriber", ] @@ -4108,7 +4127,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk", + "opentelemetry_sdk 0.20.0", +] + +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.0.0", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry-jaeger" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1" +dependencies = [ + "async-trait", + "futures-core", + "futures-util", + "opentelemetry 0.21.0", + "opentelemetry-semantic-conventions 0.13.0", + "opentelemetry_sdk 0.21.2", + "thrift", + "tokio", ] [[package]] @@ -4121,9 +4172,9 @@ dependencies = [ "futures-core", "http 0.2.9", "opentelemetry-proto", - "opentelemetry-semantic-conventions", + "opentelemetry-semantic-conventions 0.12.0", "opentelemetry_api", - "opentelemetry_sdk", + "opentelemetry_sdk 0.20.0", "prost", "thiserror", "tokio", @@ -4137,7 +4188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk", + "opentelemetry_sdk 0.20.0", "prost", "tonic", ] @@ -4148,7 +4199,16 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" dependencies = [ - "opentelemetry", + "opentelemetry 0.20.0", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" +dependencies = [ + "opentelemetry 0.21.0", ] [[package]] @@ -4180,7 +4240,7 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", - "ordered-float", + "ordered-float 3.9.2", "percent-encoding", "rand 0.8.5", "regex", @@ -4190,6 +4250,37 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "opentelemetry_sdk" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry 0.21.0", + "ordered-float 4.2.0", + "percent-encoding", + "rand 0.8.5", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-float" version = "3.9.2" @@ -4199,6 +4290,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -5836,6 +5936,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float 2.10.1", + "threadpool", +] + [[package]] name = "time" version = "0.3.23" @@ -6122,8 +6244,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" dependencies = [ "once_cell", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.20.0", + "opentelemetry_sdk 0.20.0", "smallvec", "tracing", "tracing-core", @@ -6131,6 +6253,24 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry 0.21.0", + "opentelemetry_sdk 0.21.2", + "smallvec", + "tracing", + "tracing-core", + "tracing-log 0.2.0", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -6528,6 +6668,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webdriver" version = "0.46.0" diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index ccefc57c1f7..9a0c0f6ebcb 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -6,7 +6,33 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "autonatv2_client" + +[[bin]] +name = "autonatv2_server" + [dependencies] +libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", + "identify"]} +clap = { version = "4.4.18", features = ["derive"] } +tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +rand = "0.8.5" +opentelemetry = { version = "0.21.0", optional = true } +opentelemetry_sdk = { version = "0.21.1", optional = true, features = ["rt-tokio"] } +tracing-opentelemetry = { version = "0.22.0", optional = true } +opentelemetry-jaeger = { version = "0.20.0", optional = true, features = ["rt-tokio"] } +cfg-if = "1.0.0" + +[features] +jaeger = ["opentelemetry", "opentelemetry_sdk", "tracing-opentelemetry", "opentelemetry-jaeger"] +opentelemetry = ["dep:opentelemetry"] +opentelemetry_sdk = ["dep:opentelemetry_sdk"] +tracing-opentelemetry = ["dep:tracing-opentelemetry"] +opentelemetry-jaeger = ["dep:opentelemetry-jaeger"] [lints] workspace = true diff --git a/examples/autonatv2/Dockerfile b/examples/autonatv2/Dockerfile new file mode 100644 index 00000000000..5a523649d80 --- /dev/null +++ b/examples/autonatv2/Dockerfile @@ -0,0 +1,20 @@ +FROM rust:1.75-alpine as builder + +RUN apk add musl-dev + +WORKDIR /workspace +COPY . . +RUN --mount=type=cache,target=./target \ + --mount=type=cache,target=/usr/local/cargo/registry \ + cargo build --release --package autonatv2 --bin autonatv2_server -F jaeger + +RUN --mount=type=cache,target=./target \ + mv ./target/release/autonatv2_server /usr/local/bin/autonatv2_server + +FROM alpine:latest + +COPY --from=builder /usr/local/bin/autonatv2_server /app/autonatv2_server + +EXPOSE 4884 + +ENTRYPOINT [ "/app/autonatv2_server", "-l", "4884" ] diff --git a/examples/autonatv2/docker-compose.yml b/examples/autonatv2/docker-compose.yml new file mode 100644 index 00000000000..75f44e7e6f9 --- /dev/null +++ b/examples/autonatv2/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + autonatv2: + build: + context: ../.. + dockerfile: examples/autonatv2/Dockerfile + ports: + - 4884:4884 + jaeger: + image: jaegertracing/all-in-one + ports: + - 6831:6831/udp + - 6832:6832/udp + - 16686:16686 + - 14268:14268 diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs new file mode 100644 index 00000000000..10f3f03fb6b --- /dev/null +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -0,0 +1,111 @@ +use std::{error::Error, net::Ipv4Addr, time::Duration}; + +use clap::Parser; +use libp2p::{ + autonat, + futures::StreamExt, + identify, identity, + multiaddr::Protocol, + noise, + swarm::{ + dial_opts::DialOpts, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, SwarmEvent, + }, + tcp, yamux, Multiaddr, SwarmBuilder, +}; +use rand::rngs::OsRng; +use tracing_subscriber::EnvFilter; + +#[derive(Debug, Parser)] +#[clap(name = "libp2p autonatv2 client")] +struct Opt { + /// Port where the client will listen for incoming connections. + #[clap(short = 'p', long, default_value_t = 0)] + listen_port: u16, + + /// Address of the server where want to connect to. + #[clap(short = 'a', long)] + server_address: Multiaddr, + + /// Probe interval in seconds. + #[clap(short = 't', long, default_value = "2")] + probe_interval: u64, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let opt = Opt::parse(); + + let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) + .build(); + + swarm.listen_on( + Multiaddr::empty() + .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) + .with(Protocol::Tcp(opt.listen_port)), + )?; + + swarm.dial( + DialOpts::unknown_peer_id() + .address(opt.server_address) + .build(), + )?; + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => { + println!("Listening on {address:?}"); + } + SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { + server, + tested_addr, + bytes_sent, + result: Ok(()), + })) => { + println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Everything Ok and verified."); + } + SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { + server, + tested_addr, + bytes_sent, + result: Err(e), + })) => { + println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Failed with {e:?}."); + } + SwarmEvent::ExternalAddrConfirmed { address } => { + println!("External address confirmed: {address}"); + } + _ => {} + } + } +} + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + autonat: autonat::v2::client::Behaviour, + identify: identify::Behaviour, +} + +impl Behaviour { + pub fn new(key: identity::PublicKey, probe_interval: u64) -> Self { + Self { + autonat: autonat::v2::client::Behaviour::new( + OsRng, + autonat::v2::client::Config::default() + .with_probe_interval(Duration::from_secs(probe_interval)), + ), + identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), + } + } +} diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs new file mode 100644 index 00000000000..46815d810e2 --- /dev/null +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -0,0 +1,86 @@ +use std::{error::Error, net::Ipv4Addr, time::Duration}; + +use cfg_if::cfg_if; +use clap::Parser; +use libp2p::{ + autonat, + futures::{task::waker, StreamExt}, + identify, identity, + multiaddr::Protocol, + noise, + swarm::{NetworkBehaviour, SwarmEvent}, + tcp, yamux, Multiaddr, SwarmBuilder, +}; +use rand::rngs::OsRng; +use tracing_subscriber::{util::SubscriberInitExt, EnvFilter}; + +#[derive(Debug, Parser)] +#[clap(name = "libp2p autonatv2 server")] +struct Opt { + #[clap(short, long, default_value_t = 0)] + listen_port: u16, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + cfg_if! { + if #[cfg(feature = "jaeger")] { + use tracing_subscriber::layer::SubscriberExt; + use opentelemetry_sdk::runtime::Tokio; + let tracer = opentelemetry_jaeger::new_agent_pipeline() + .with_endpoint("jaeger:6831") + .with_service_name("autonatv2") + .install_batch(Tokio)?; + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + let subscriber = tracing_subscriber::Registry::default() + .with(telemetry); + } else { + let subscriber = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .finish(); + } + } + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + let opt = Opt::parse(); + + let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_behaviour(|key| Behaviour::new(key.public()))? + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) + .build(); + + swarm.listen_on( + Multiaddr::empty() + .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) + .with(Protocol::Tcp(opt.listen_port)), + )?; + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), + SwarmEvent::Behaviour(event) => println!("{event:?}"), + e => println!("{e:?}"), + } + } +} + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + autonat: autonat::v2::server::Behaviour, + identify: identify::Behaviour, +} + +impl Behaviour { + pub fn new(key: identity::PublicKey) -> Self { + Self { + autonat: autonat::v2::server::Behaviour::new(OsRng), + identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), + } + } +} diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/autonatv2/src/main.rs b/examples/autonatv2/src/main.rs deleted file mode 100644 index e7a11a969c0..00000000000 --- a/examples/autonatv2/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} From d723c50dac8829e3f1b07249cef43c6a414234f6 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:33:57 +0100 Subject: [PATCH 66/97] Port quic --- Cargo.lock | 2 +- transports/quic/src/transport.rs | 201 ++++++++++++++++--------------- 2 files changed, 107 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd8f347e37e..818da4fa2e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4139,7 +4139,7 @@ checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ "futures-core", "futures-sink", - "indexmap 2.0.0", + "indexmap 2.2.1", "js-sys", "once_cell", "pin-project-lite", diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index aea3c91093f..f2656ed505d 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -31,6 +31,8 @@ use futures::{prelude::*, stream::SelectAll}; use if_watch::IfEvent; +use libp2p_core::transport::{DialOpts, PortUse}; +use libp2p_core::Endpoint; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, @@ -256,110 +258,119 @@ impl Transport for GenTransport

{ Some(observed.clone()) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let (socket_addr, version, _peer_id) = self.remote_multiaddr_to_socketaddr(addr, true)?; - - let endpoint = match self.eligible_listener(&socket_addr) { - None => { - // No listener. Get or create an explicit dialer. - let socket_family = socket_addr.ip().into(); - let dialer = match self.dialer.entry(socket_family) { - Entry::Occupied(occupied) => occupied.get().clone(), - Entry::Vacant(vacant) => { - if let Some(waker) = self.waker.take() { - waker.wake(); - } - let listen_socket_addr = match socket_family { - SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), - }; - let socket = - UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; - let endpoint_config = self.quinn_config.endpoint_config.clone(); - let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; - - vacant.insert(endpoint.clone()); - endpoint - } - }; - dialer - } - Some(listener) => listener.endpoint.clone(), - }; - let handshake_timeout = self.handshake_timeout; - let mut client_config = self.quinn_config.client_config.clone(); - if version == ProtocolVersion::Draft29 { - client_config.version(0xff00_001d); - } - Ok(Box::pin(async move { - // This `"l"` seems necessary because an empty string is an invalid domain - // name. While we don't use domain names, the underlying rustls library - // is based upon the assumption that we do. - let connecting = endpoint - .connect_with(client_config, socket_addr, "l") - .map_err(ConnectError)?; - Connecting::new(connecting, handshake_timeout).await - })) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - let (socket_addr, _version, peer_id) = + let (socket_addr, version, peer_id) = self.remote_multiaddr_to_socketaddr(addr.clone(), true)?; - let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?; - - let socket = self - .eligible_listener(&socket_addr) - .ok_or(TransportError::Other( - Error::NoActiveListenerForDialAsListener, - ))? - .try_clone_socket() - .map_err(Self::Error::from)?; - - tracing::debug!("Preparing for hole-punch from {addr}"); - - let hole_puncher = hole_puncher::

(socket, socket_addr, self.handshake_timeout); - - let (sender, receiver) = oneshot::channel(); - - match self.hole_punch_attempts.entry(socket_addr) { - Entry::Occupied(mut sender_entry) => { - // Stale senders, i.e. from failed hole punches are not removed. - // Thus, we can just overwrite a stale sender. - if !sender_entry.get().is_canceled() { - return Err(TransportError::Other(Error::HolePunchInProgress( - socket_addr, - ))); + + match (dial_opts.role, dial_opts.port_use) { + (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::New) => { + let endpoint = match self.eligible_listener(&socket_addr) { + None => { + // No listener. Get or create an explicit dialer. + let socket_family = socket_addr.ip().into(); + let dialer = match self.dialer.entry(socket_family) { + Entry::Occupied(occupied) => occupied.get().clone(), + Entry::Vacant(vacant) => { + if let Some(waker) = self.waker.take() { + waker.wake(); + } + let listen_socket_addr = match socket_family { + SocketFamily::Ipv4 => { + SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0) + } + SocketFamily::Ipv6 => { + SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0) + } + }; + let socket = UdpSocket::bind(listen_socket_addr) + .map_err(Self::Error::from)?; + let endpoint_config = self.quinn_config.endpoint_config.clone(); + let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; + + vacant.insert(endpoint.clone()); + endpoint + } + }; + dialer + } + Some(listener) => listener.endpoint.clone(), + }; + let handshake_timeout = self.handshake_timeout; + let mut client_config = self.quinn_config.client_config.clone(); + if version == ProtocolVersion::Draft29 { + client_config.version(0xff00_001d); } - sender_entry.insert(sender); + Ok(Box::pin(async move { + // This `"l"` seems necessary because an empty string is an invalid domain + // name. While we don't use domain names, the underlying rustls library + // is based upon the assumption that we do. + let connecting = endpoint + .connect_with(client_config, socket_addr, "l") + .map_err(ConnectError)?; + Connecting::new(connecting, handshake_timeout).await + })) } - Entry::Vacant(entry) => { - entry.insert(sender); - } - }; + (Endpoint::Listener, _) => { + let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?; + + let socket = self + .eligible_listener(&socket_addr) + .ok_or(TransportError::Other( + Error::NoActiveListenerForDialAsListener, + ))? + .try_clone_socket() + .map_err(Self::Error::from)?; + + tracing::debug!("Preparing for hole-punch from {addr}"); + + let hole_puncher = hole_puncher::

(socket, socket_addr, self.handshake_timeout); + + let (sender, receiver) = oneshot::channel(); + + match self.hole_punch_attempts.entry(socket_addr) { + Entry::Occupied(mut sender_entry) => { + // Stale senders, i.e. from failed hole punches are not removed. + // Thus, we can just overwrite a stale sender. + if !sender_entry.get().is_canceled() { + return Err(TransportError::Other(Error::HolePunchInProgress( + socket_addr, + ))); + } + sender_entry.insert(sender); + } + Entry::Vacant(entry) => { + entry.insert(sender); + } + }; - Ok(Box::pin(async move { - futures::pin_mut!(hole_puncher); - match futures::future::select(receiver, hole_puncher).await { - Either::Left((message, _)) => { - let (inbound_peer_id, connection) = message - .expect("hole punch connection sender is never dropped before receiver") - .await?; - if inbound_peer_id != peer_id { - tracing::warn!( - peer=%peer_id, - inbound_peer=%inbound_peer_id, - socket_address=%socket_addr, - "expected inbound connection from socket_address to resolve to peer but got inbound peer" - ); + Ok(Box::pin(async move { + futures::pin_mut!(hole_puncher); + match futures::future::select(receiver, hole_puncher).await { + Either::Left((message, _)) => { + let (inbound_peer_id, connection) = message + .expect( + "hole punch connection sender is never dropped before receiver", + ) + .await?; + if inbound_peer_id != peer_id { + tracing::warn!( + peer=%peer_id, + inbound_peer=%inbound_peer_id, + socket_address=%socket_addr, + "expected inbound connection from socket_address to resolve to peer but got inbound peer" + ); + } + Ok((inbound_peer_id, connection)) + } + Either::Right((hole_punch_err, _)) => Err(hole_punch_err), } - Ok((inbound_peer_id, connection)) - } - Either::Right((hole_punch_err, _)) => Err(hole_punch_err), + })) } - })) + } } fn poll( From daf2a0b6a379ce2d9bbec3a973734321e76a57ec Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:42:54 +0100 Subject: [PATCH 67/97] Make it compile --- misc/memory-connection-limits/src/lib.rs | 3 +- misc/metrics/src/bandwidth.rs | 17 ++----- protocols/dcutr/src/behaviour.rs | 3 ++ protocols/floodsub/src/layer.rs | 2 + protocols/gossipsub/src/behaviour.rs | 5 +- protocols/mdns/src/behaviour.rs | 2 + protocols/perf/src/client/behaviour.rs | 3 +- protocols/perf/src/server/behaviour.rs | 2 + protocols/relay/src/behaviour.rs | 3 ++ protocols/relay/src/priv_client.rs | 2 + protocols/relay/src/priv_client/transport.rs | 30 +++++------ protocols/rendezvous/src/client.rs | 11 +++- protocols/rendezvous/src/server.rs | 4 +- protocols/stream/src/behaviour.rs | 3 +- protocols/upnp/src/behaviour.rs | 3 +- transports/webrtc-websys/src/transport.rs | 18 ++++--- transports/webrtc/src/tokio/transport.rs | 23 ++++----- transports/websocket-websys/src/lib.rs | 18 ++++--- .../webtransport-websys/src/transport.rs | 21 ++++---- wasm-tests/webtransport-tests/src/lib.rs | 51 +++++++++++++++++-- 20 files changed, 144 insertions(+), 80 deletions(-) diff --git a/misc/memory-connection-limits/src/lib.rs b/misc/memory-connection-limits/src/lib.rs index ac911654979..943ce916b70 100644 --- a/misc/memory-connection-limits/src/lib.rs +++ b/misc/memory-connection-limits/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ dummy, ConnectionDenied, ConnectionId, FromSwarm, NetworkBehaviour, THandler, THandlerInEvent, @@ -174,6 +174,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/misc/metrics/src/bandwidth.rs b/misc/metrics/src/bandwidth.rs index 2792e00612c..39ee1be4854 100644 --- a/misc/metrics/src/bandwidth.rs +++ b/misc/metrics/src/bandwidth.rs @@ -7,7 +7,7 @@ use futures::{ }; use libp2p_core::{ muxing::{StreamMuxer, StreamMuxerEvent}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, Multiaddr, }; use libp2p_identity::PeerId; @@ -84,24 +84,15 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let metrics = ConnectionMetrics::from_family_and_addr(&self.metrics, &addr); - Ok(self - .transport - .dial(addr.clone())? - .map_ok(Box::new(|(peer_id, stream_muxer)| { - (peer_id, Muxer::new(stream_muxer, metrics)) - }))) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { let metrics = ConnectionMetrics::from_family_and_addr(&self.metrics, &addr); Ok(self .transport - .dial_as_listener(addr.clone())? + .dial(addr.clone(), dial_opts)? .map_ok(Box::new(|(peer_id, stream_muxer)| { (peer_id, Muxer::new(stream_muxer, metrics)) }))) diff --git a/protocols/dcutr/src/behaviour.rs b/protocols/dcutr/src/behaviour.rs index 3742eb512f5..5ec5fe5568e 100644 --- a/protocols/dcutr/src/behaviour.rs +++ b/protocols/dcutr/src/behaviour.rs @@ -24,6 +24,7 @@ use crate::{handler, protocol}; use either::Either; use libp2p_core::connection::ConnectedPoint; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, DialFailure, FromSwarm}; @@ -206,12 +207,14 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { if is_relayed(addr) { return Ok(Either::Left(handler::relayed::Handler::new( ConnectedPoint::Dialer { address: addr.clone(), role_override, + port_use, }, self.observed_addresses(), ))); // TODO: We could make two `handler::relayed::Handler` here, one inbound one outbound. diff --git a/protocols/floodsub/src/layer.rs b/protocols/floodsub/src/layer.rs index 35711408a8d..1a70d2213b2 100644 --- a/protocols/floodsub/src/layer.rs +++ b/protocols/floodsub/src/layer.rs @@ -27,6 +27,7 @@ use crate::FloodsubConfig; use bytes::Bytes; use cuckoofilter::{CuckooError, CuckooFilter}; use fnv::FnvHashSet; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, FromSwarm}; @@ -346,6 +347,7 @@ impl NetworkBehaviour for Floodsub { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Default::default()) } diff --git a/protocols/gossipsub/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index 24a32de4cc7..a981f25c851 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -35,7 +35,9 @@ use prometheus_client::registry::Registry; use rand::{seq::SliceRandom, thread_rng}; use instant::Instant; -use libp2p_core::{multiaddr::Protocol::Ip4, multiaddr::Protocol::Ip6, Endpoint, Multiaddr}; +use libp2p_core::{ + multiaddr::Protocol::Ip4, multiaddr::Protocol::Ip6, transport::PortUse, Endpoint, Multiaddr, +}; use libp2p_identity::Keypair; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -3011,6 +3013,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new(self.config.protocol_config())) } diff --git a/protocols/mdns/src/behaviour.rs b/protocols/mdns/src/behaviour.rs index 4e3533f26ab..5bcbaaeff8d 100644 --- a/protocols/mdns/src/behaviour.rs +++ b/protocols/mdns/src/behaviour.rs @@ -28,6 +28,7 @@ use crate::Config; use futures::channel::mpsc; use futures::{Stream, StreamExt}; use if_watch::IfEvent; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::FromSwarm; @@ -262,6 +263,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/protocols/perf/src/client/behaviour.rs b/protocols/perf/src/client/behaviour.rs index 880bcdd9c83..7dd0559010c 100644 --- a/protocols/perf/src/client/behaviour.rs +++ b/protocols/perf/src/client/behaviour.rs @@ -25,7 +25,7 @@ use std::{ task::{Context, Poll}, }; -use libp2p_core::Multiaddr; +use libp2p_core::{transport::PortUse, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ derive_prelude::ConnectionEstablished, ConnectionClosed, ConnectionId, FromSwarm, @@ -92,6 +92,7 @@ impl NetworkBehaviour for Behaviour { _peer: PeerId, _addr: &Multiaddr, _role_override: libp2p_core::Endpoint, + _port_use: PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(Handler::default()) } diff --git a/protocols/perf/src/server/behaviour.rs b/protocols/perf/src/server/behaviour.rs index da24d763606..5408029e85d 100644 --- a/protocols/perf/src/server/behaviour.rs +++ b/protocols/perf/src/server/behaviour.rs @@ -25,6 +25,7 @@ use std::{ task::{Context, Poll}, }; +use libp2p_core::transport::PortUse; use libp2p_identity::PeerId; use libp2p_swarm::{ ConnectionId, FromSwarm, NetworkBehaviour, THandlerInEvent, THandlerOutEvent, ToSwarm, @@ -71,6 +72,7 @@ impl NetworkBehaviour for Behaviour { _peer: PeerId, _addr: &libp2p_core::Multiaddr, _role_override: libp2p_core::Endpoint, + _port_use: PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(Handler::default()) } diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index df8443e8359..6282280d21c 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -29,6 +29,7 @@ use crate::protocol::{inbound_hop, outbound_stop}; use either::Either; use instant::Instant; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, FromSwarm}; @@ -328,6 +329,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { if addr.is_relayed() { // Deny all substreams on relayed connection. @@ -343,6 +345,7 @@ impl NetworkBehaviour for Behaviour { ConnectedPoint::Dialer { address: addr.clone(), role_override, + port_use, }, ))) } diff --git a/protocols/relay/src/priv_client.rs b/protocols/relay/src/priv_client.rs index e414852ef81..f8d1d9c9eb2 100644 --- a/protocols/relay/src/priv_client.rs +++ b/protocols/relay/src/priv_client.rs @@ -34,6 +34,7 @@ use futures::io::{AsyncRead, AsyncWrite}; use futures::ready; use futures::stream::StreamExt; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, FromSwarm}; @@ -178,6 +179,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { if addr.is_relayed() { return Ok(Either::Right(dummy::ConnectionHandler)); diff --git a/protocols/relay/src/priv_client/transport.rs b/protocols/relay/src/priv_client/transport.rs index 7147f0b5e55..c4cc4b6a6d0 100644 --- a/protocols/relay/src/priv_client/transport.rs +++ b/protocols/relay/src/priv_client/transport.rs @@ -32,7 +32,7 @@ use futures::sink::SinkExt; use futures::stream::SelectAll; use futures::stream::{Stream, StreamExt}; use libp2p_core::multiaddr::{Multiaddr, Protocol}; -use libp2p_core::transport::{ListenerId, TransportError, TransportEvent}; +use libp2p_core::transport::{DialOpts, ListenerId, TransportError, TransportEvent}; use libp2p_identity::PeerId; use std::collections::VecDeque; use std::pin::Pin; @@ -165,7 +165,19 @@ impl libp2p_core::Transport for Transport { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + // [`Endpoint::Listener`] is used for NAT and firewall + // traversal. One would coordinate such traversal via a previously + // established relayed connection, but never using a relayed connection + // itself. + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let RelayedMultiaddr { relay_peer_id, relay_addr, @@ -198,20 +210,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> - where - Self: Sized, - { - // [`Transport::dial_as_listener`] is used for NAT and firewall - // traversal. One would coordinate such traversal via a previously - // established relayed connection, but never using a relayed connection - // itself. - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { None } diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 92d7884758b..a794252ff0b 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -24,6 +24,7 @@ use futures::future::BoxFuture; use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::StreamExt; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr, PeerRecord}; use libp2p_identity::{Keypair, PeerId, SigningError}; use libp2p_request_response::{OutboundRequestId, ProtocolSupport}; @@ -208,9 +209,15 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_connection_handler_event( diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 667c71e20e3..60dc067cbba 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -24,6 +24,7 @@ use bimap::BiMap; use futures::future::BoxFuture; use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_request_response::ProtocolSupport; @@ -140,9 +141,10 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) } fn on_connection_handler_event( diff --git a/protocols/stream/src/behaviour.rs b/protocols/stream/src/behaviour.rs index e02aca884b7..07549ccef54 100644 --- a/protocols/stream/src/behaviour.rs +++ b/protocols/stream/src/behaviour.rs @@ -5,7 +5,7 @@ use std::{ }; use futures::{channel::mpsc, StreamExt}; -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ self as swarm, dial_opts::DialOpts, ConnectionDenied, ConnectionId, FromSwarm, @@ -82,6 +82,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new( peer, diff --git a/protocols/upnp/src/behaviour.rs b/protocols/upnp/src/behaviour.rs index a94ef9526dd..70d6afbffcc 100644 --- a/protocols/upnp/src/behaviour.rs +++ b/protocols/upnp/src/behaviour.rs @@ -36,7 +36,7 @@ use crate::tokio::{is_addr_global, Gateway}; use futures::{channel::oneshot, Future, StreamExt}; use futures_timer::Delay; use igd_next::PortMappingProtocol; -use libp2p_core::{multiaddr, transport::ListenerId, Endpoint, Multiaddr}; +use libp2p_core::{multiaddr, transport::{ListenerId, PortUse}, Endpoint, Multiaddr}; use libp2p_swarm::{ derive_prelude::PeerId, dummy, ConnectionDenied, ConnectionId, ExpiredListenAddr, FromSwarm, NetworkBehaviour, NewListenAddr, ToSwarm, @@ -248,6 +248,7 @@ impl NetworkBehaviour for Behaviour { _peer: PeerId, _addr: &Multiaddr, _role_override: Endpoint, + _port_use: PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/transports/webrtc-websys/src/transport.rs b/transports/webrtc-websys/src/transport.rs index ecf137eab8a..16faa95fb14 100644 --- a/transports/webrtc-websys/src/transport.rs +++ b/transports/webrtc-websys/src/transport.rs @@ -4,6 +4,7 @@ use super::Error; use futures::future::FutureExt; use libp2p_core::multiaddr::Multiaddr; use libp2p_core::muxing::StreamMuxerBox; +use libp2p_core::transport::DialOpts; use libp2p_core::transport::{Boxed, ListenerId, Transport as _, TransportError, TransportEvent}; use libp2p_identity::{Keypair, PeerId}; use std::future::Future; @@ -62,7 +63,15 @@ impl libp2p_core::Transport for Transport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + if maybe_local_firefox() { return Err(TransportError::Other( "Firefox does not support WebRTC over localhost or 127.0.0.1" @@ -89,13 +98,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/transports/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index 02cfa6f7296..70e8e10b9d8 100644 --- a/transports/webrtc/src/tokio/transport.rs +++ b/transports/webrtc/src/tokio/transport.rs @@ -22,7 +22,7 @@ use futures::{future::BoxFuture, prelude::*, stream::SelectAll, stream::Stream}; use if_watch::{tokio::IfWatcher, IfEvent}; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; use libp2p_identity as identity; use libp2p_identity::PeerId; @@ -118,7 +118,15 @@ impl libp2p_core::Transport for Transport { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + _: DialOpts, + ) -> Result> { + // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the + // `addr`. See DCUtR specification below. + // + // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol let (sock_addr, server_fingerprint) = libp2p_webrtc_utils::parse_webrtc_dial_addr(&addr) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if sock_addr.port() == 0 || sock_addr.ip().is_unspecified() { @@ -151,17 +159,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the - // `addr`. See DCUtR specification below. - // - // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol - self.dial(addr) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { libp2p_core::address_translation(server, observed) } diff --git a/transports/websocket-websys/src/lib.rs b/transports/websocket-websys/src/lib.rs index 5c1a6ebf1c4..87403365abd 100644 --- a/transports/websocket-websys/src/lib.rs +++ b/transports/websocket-websys/src/lib.rs @@ -26,6 +26,7 @@ use bytes::BytesMut; use futures::task::AtomicWaker; use futures::{future::Ready, io, prelude::*}; use js_sys::Array; +use libp2p_core::transport::DialOpts; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, @@ -86,7 +87,15 @@ impl libp2p_core::Transport for Transport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let url = extract_websocket_url(&addr) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr))?; @@ -101,13 +110,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/transports/webtransport-websys/src/transport.rs b/transports/webtransport-websys/src/transport.rs index cb556ffef99..9f828922182 100644 --- a/transports/webtransport-websys/src/transport.rs +++ b/transports/webtransport-websys/src/transport.rs @@ -1,6 +1,8 @@ use futures::future::FutureExt; use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::transport::{Boxed, ListenerId, Transport as _, TransportError, TransportEvent}; +use libp2p_core::transport::{ + Boxed, DialOpts, ListenerId, Transport as _, TransportError, TransportEvent, +}; use libp2p_identity::{Keypair, PeerId}; use multiaddr::Multiaddr; use std::future::Future; @@ -62,7 +64,15 @@ impl libp2p_core::Transport for Transport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let endpoint = Endpoint::from_multiaddr(&addr).map_err(|e| match e { e @ Error::InvalidMultiaddr(_) => { tracing::warn!("{}", e); @@ -83,13 +93,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index 1f420cd6671..ad160f19d91 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -1,7 +1,8 @@ use futures::channel::oneshot; use futures::{AsyncReadExt, AsyncWriteExt}; use getrandom::getrandom; -use libp2p_core::{StreamMuxer, Transport as _}; +use libp2p_core::transport::{DialOpts, PortUse}; +use libp2p_core::{Endpoint, StreamMuxer, Transport as _}; use libp2p_identity::{Keypair, PeerId}; use libp2p_noise as noise; use libp2p_webtransport_websys::{Config, Connection, Error, Stream, Transport}; @@ -263,7 +264,17 @@ async fn connect_without_peer_id() { addr.pop(); let mut transport = Transport::new(Config::new(&keypair)); - transport.dial(addr).unwrap().await.unwrap(); + transport + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); } #[wasm_bindgen_test] @@ -278,7 +289,17 @@ async fn error_on_unknown_peer_id() { addr.push(Protocol::P2p(PeerId::random())); let mut transport = Transport::new(Config::new(&keypair)); - let e = transport.dial(addr.clone()).unwrap().await.unwrap_err(); + let e = transport + .dial( + addr.clone(), + DialOpts { + role: Endpoint::Listener, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap_err(); assert!(matches!(e, Error::UnknownRemotePeerId)); } @@ -297,7 +318,17 @@ async fn error_on_unknown_certhash() { addr.push(peer_id); let mut transport = Transport::new(Config::new(&keypair)); - let e = transport.dial(addr.clone()).unwrap().await.unwrap_err(); + let e = transport + .dial( + addr.clone(), + DialOpts { + role: Endpoint::Listener, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap_err(); assert!(matches!( e, Error::Noise(noise::Error::UnknownWebTransportCerthashes(..)) @@ -310,7 +341,17 @@ async fn new_connection_to_echo_server() -> Connection { let mut transport = Transport::new(Config::new(&keypair)); - let (_peer_id, conn) = transport.dial(addr).unwrap().await.unwrap(); + let (_peer_id, conn) = transport + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); conn } From 6b85b82006cecbc01a697c06228ba118a4d4ba26 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:53:44 +0100 Subject: [PATCH 68/97] Allow deprecated items --- examples/autonat/src/bin/autonat_client.rs | 4 +++- examples/autonat/src/bin/autonat_server.rs | 2 ++ examples/autonatv2/src/bin/autonatv2_client.rs | 4 +--- examples/autonatv2/src/bin/autonatv2_server.rs | 5 ++--- examples/dcutr/src/main.rs | 2 +- hole-punching-tests/src/main.rs | 2 +- misc/server/src/behaviour.rs | 7 +++++-- misc/server/src/main.rs | 2 +- transports/uds/src/lib.rs | 11 ++--------- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 3fb25aa6222..42b3995b57b 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -20,6 +20,8 @@ #![doc = include_str!("../../README.md")] +#![allow(deprecated)] + use clap::Parser; use futures::StreamExt; use libp2p::core::multiaddr::Protocol; @@ -86,7 +88,7 @@ async fn main() -> Result<(), Box> { #[derive(NetworkBehaviour)] struct Behaviour { identify: identify::Behaviour, - auto_nat: autonat::Behaviour, + auto_nat: autonat::v1::Behaviour, } impl Behaviour { diff --git a/examples/autonat/src/bin/autonat_server.rs b/examples/autonat/src/bin/autonat_server.rs index 44a53f0d17f..7545b8dac06 100644 --- a/examples/autonat/src/bin/autonat_server.rs +++ b/examples/autonat/src/bin/autonat_server.rs @@ -20,6 +20,8 @@ #![doc = include_str!("../../README.md")] +#![allow(deprecated)] + use clap::Parser; use futures::StreamExt; use libp2p::core::{multiaddr::Protocol, Multiaddr}; diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs index 10f3f03fb6b..8f9ce9e5eee 100644 --- a/examples/autonatv2/src/bin/autonatv2_client.rs +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -7,9 +7,7 @@ use libp2p::{ identify, identity, multiaddr::Protocol, noise, - swarm::{ - dial_opts::DialOpts, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, SwarmEvent, - }, + swarm::{dial_opts::DialOpts, NetworkBehaviour, SwarmEvent}, tcp, yamux, Multiaddr, SwarmBuilder, }; use rand::rngs::OsRng; diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs index 46815d810e2..cf1798031ec 100644 --- a/examples/autonatv2/src/bin/autonatv2_server.rs +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -4,7 +4,7 @@ use cfg_if::cfg_if; use clap::Parser; use libp2p::{ autonat, - futures::{task::waker, StreamExt}, + futures::StreamExt, identify, identity, multiaddr::Protocol, noise, @@ -12,7 +12,6 @@ use libp2p::{ tcp, yamux, Multiaddr, SwarmBuilder, }; use rand::rngs::OsRng; -use tracing_subscriber::{util::SubscriberInitExt, EnvFilter}; #[derive(Debug, Parser)] #[clap(name = "libp2p autonatv2 server")] @@ -36,7 +35,7 @@ async fn main() -> Result<(), Box> { .with(telemetry); } else { let subscriber = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .finish(); } } diff --git a/examples/dcutr/src/main.rs b/examples/dcutr/src/main.rs index 51df670f8a7..630d4b2b1f3 100644 --- a/examples/dcutr/src/main.rs +++ b/examples/dcutr/src/main.rs @@ -89,7 +89,7 @@ async fn main() -> Result<(), Box> { libp2p::SwarmBuilder::with_existing_identity(generate_ed25519(opts.secret_key_seed)) .with_tokio() .with_tcp( - tcp::Config::default().port_reuse(true).nodelay(true), + tcp::Config::default().nodelay(true), noise::Config::new, yamux::Config::default, )? diff --git a/hole-punching-tests/src/main.rs b/hole-punching-tests/src/main.rs index 4f81cd65480..ec8a57763f2 100644 --- a/hole-punching-tests/src/main.rs +++ b/hole-punching-tests/src/main.rs @@ -64,7 +64,7 @@ async fn main() -> Result<()> { let mut swarm = libp2p::SwarmBuilder::with_new_identity() .with_tokio() .with_tcp( - tcp::Config::new().port_reuse(true).nodelay(true), + tcp::Config::new().nodelay(true), noise::Config::new, yamux::Config::default, )? diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index ec025e02129..811b103c9c7 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use libp2p::autonat; use libp2p::identify; use libp2p::kad; @@ -8,6 +10,7 @@ use libp2p::{identity, swarm::NetworkBehaviour, Multiaddr, PeerId}; use std::str::FromStr; use std::time::Duration; + const BOOTNODES: [&str; 4] = [ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", @@ -21,7 +24,7 @@ pub(crate) struct Behaviour { ping: ping::Behaviour, identify: identify::Behaviour, pub(crate) kademlia: Toggle>, - autonat: Toggle, + autonat: Toggle, } impl Behaviour { @@ -54,7 +57,7 @@ impl Behaviour { .into(); let autonat = if enable_autonat { - Some(autonat::Behaviour::new( + Some(autonat::v1::Behaviour::new( PeerId::from(pub_key.clone()), Default::default(), )) diff --git a/misc/server/src/main.rs b/misc/server/src/main.rs index 16e6530e946..205466c8e16 100644 --- a/misc/server/src/main.rs +++ b/misc/server/src/main.rs @@ -76,7 +76,7 @@ async fn main() -> Result<(), Box> { let mut swarm = libp2p::SwarmBuilder::with_existing_identity(local_keypair) .with_tokio() .with_tcp( - tcp::Config::default().port_reuse(true).nodelay(true), + tcp::Config::default().nodelay(true), noise::Config::new, yamux::Config::default, )? diff --git a/transports/uds/src/lib.rs b/transports/uds/src/lib.rs index 075cbadb80a..d8acabd008a 100644 --- a/transports/uds/src/lib.rs +++ b/transports/uds/src/lib.rs @@ -46,7 +46,7 @@ use futures::{ use libp2p_core::transport::ListenerId; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{TransportError, TransportEvent}, + transport::{TransportError, TransportEvent, DialOpts}, Transport, }; use std::collections::VecDeque; @@ -159,7 +159,7 @@ macro_rules! codegen { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, _dial_opts: DialOpts) -> Result> { // TODO: Should we dial at all? if let Ok(path) = multiaddr_to_path(&addr) { tracing::debug!(address=%addr, "Dialing address"); @@ -169,13 +169,6 @@ macro_rules! codegen { } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - fn address_translation( &self, _server: &Multiaddr, From 0b94a0126c785f489c0ffb043136cbb54506dd31 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:22:56 +0100 Subject: [PATCH 69/97] Make the tests run --- misc/connection-limits/src/lib.rs | 1 + misc/memory-connection-limits/tests/util.rs | 3 +- protocols/gossipsub/src/behaviour/tests.rs | 6 ++ protocols/kad/src/behaviour/test.rs | 3 + protocols/ping/src/protocol.rs | 11 ++- swarm/tests/connection_close.rs | 2 + swarm/tests/swarm_derive.rs | 1 + transports/dns/src/lib.rs | 37 +++++----- transports/quic/src/transport.rs | 8 ++- transports/quic/tests/smoke.rs | 77 ++++++++++++++++----- transports/quic/tests/stream_compliance.rs | 14 +++- transports/tcp/src/lib.rs | 1 - transports/uds/src/lib.rs | 18 +++-- transports/webrtc/tests/smoke.rs | 16 ++++- transports/websocket/src/lib.rs | 14 +++- 15 files changed, 160 insertions(+), 52 deletions(-) diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index e31f50b83cb..b02e52f25a1 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -570,6 +570,7 @@ mod tests { _peer: PeerId, _addr: &Multiaddr, _role_override: Endpoint, + _port_use: PortUse, ) -> Result, ConnectionDenied> { Err(ConnectionDenied::new(std::io::Error::new( std::io::ErrorKind::Other, diff --git a/misc/memory-connection-limits/tests/util.rs b/misc/memory-connection-limits/tests/util.rs index f40ce319929..d18aa78fd22 100644 --- a/misc/memory-connection-limits/tests/util.rs +++ b/misc/memory-connection-limits/tests/util.rs @@ -20,7 +20,7 @@ use std::task::{Context, Poll}; -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ dummy, ConnectionDenied, ConnectionId, FromSwarm, NetworkBehaviour, THandler, THandlerInEvent, @@ -102,6 +102,7 @@ impl NetworkBehaviour _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { self.handle_established(); Ok(dummy::ConnectionHandler) diff --git a/protocols/gossipsub/src/behaviour/tests.rs b/protocols/gossipsub/src/behaviour/tests.rs index 2bf1c90c5c8..49da7b3f27d 100644 --- a/protocols/gossipsub/src/behaviour/tests.rs +++ b/protocols/gossipsub/src/behaviour/tests.rs @@ -211,6 +211,7 @@ where ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, } } else { ConnectedPoint::Listener { @@ -261,6 +262,7 @@ where let fake_endpoint = ConnectedPoint::Dialer { address: Multiaddr::empty(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }; // this is not relevant // peer_connections.connections should never be empty. @@ -566,6 +568,7 @@ fn test_join() { endpoint: &ConnectedPoint::Dialer { address: "/ip4/127.0.0.1".parse::().unwrap(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 0, @@ -4056,6 +4059,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 0, @@ -4077,6 +4081,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr2.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 1, @@ -4107,6 +4112,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 2, diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 522eebcba92..48a4c76c5aa 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -1302,6 +1302,7 @@ fn network_behaviour_on_address_change() { let endpoint = ConnectedPoint::Dialer { address: old_address.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }; // Mimick a connection being established. @@ -1352,10 +1353,12 @@ fn network_behaviour_on_address_change() { old: &ConnectedPoint::Dialer { address: old_address, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, new: &ConnectedPoint::Dialer { address: new_address.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, })); diff --git a/protocols/ping/src/protocol.rs b/protocols/ping/src/protocol.rs index 28549e1c198..38535e55621 100644 --- a/protocols/ping/src/protocol.rs +++ b/protocols/ping/src/protocol.rs @@ -87,7 +87,8 @@ mod tests { use futures::StreamExt; use libp2p_core::{ multiaddr::multiaddr, - transport::{memory::MemoryTransport, ListenerId, Transport}, + transport::{memory::MemoryTransport, DialOpts, ListenerId, PortUse, Transport}, + Endpoint, }; use rand::{thread_rng, Rng}; use std::time::Duration; @@ -113,7 +114,13 @@ mod tests { async_std::task::block_on(async move { let c = MemoryTransport::new() - .dial(listener_addr) + .dial( + listener_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) .unwrap() .await .unwrap(); diff --git a/swarm/tests/connection_close.rs b/swarm/tests/connection_close.rs index 4efe8d17e49..4d530f47684 100644 --- a/swarm/tests/connection_close.rs +++ b/swarm/tests/connection_close.rs @@ -1,3 +1,4 @@ +use libp2p_core::transport::PortUse; use libp2p_core::upgrade::DeniedUpgrade; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -66,6 +67,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(HandlerWithState { precious_state: self.state, diff --git a/swarm/tests/swarm_derive.rs b/swarm/tests/swarm_derive.rs index 6663cf906b2..a0e6662ed19 100644 --- a/swarm/tests/swarm_derive.rs +++ b/swarm/tests/swarm_derive.rs @@ -404,6 +404,7 @@ fn with_generics_constrained() { _: libp2p_identity::PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index d5a61972d1c..a3701e5b537 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -624,8 +624,8 @@ mod tests { use futures::future::BoxFuture; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{TransportError, TransportEvent}, - Transport, + transport::{PortUse, TransportError, TransportEvent}, + Endpoint, Transport, }; use libp2p_identity::PeerId; @@ -656,7 +656,11 @@ mod tests { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + _: DialOpts, + ) -> Result> { // Check that all DNS components have been resolved, i.e. replaced. assert!(!addr.iter().any(|p| matches!( p, @@ -665,13 +669,6 @@ mod tests { Ok(Box::pin(future::ready(Ok(())))) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - fn address_translation(&self, _: &Multiaddr, _: &Multiaddr) -> Option { None } @@ -691,30 +688,34 @@ mod tests { T::Dial: Send, R: Clone + Send + Sync + Resolver + 'static, { + let dial_opts = DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }; // Success due to existing A record for example.com. let _ = transport - .dial("/dns4/example.com/tcp/20000".parse().unwrap()) + .dial("/dns4/example.com/tcp/20000".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); // Success due to existing AAAA record for example.com. let _ = transport - .dial("/dns6/example.com/tcp/20000".parse().unwrap()) + .dial("/dns6/example.com/tcp/20000".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); // Success due to pass-through, i.e. nothing to resolve. let _ = transport - .dial("/ip4/1.2.3.4/tcp/20000".parse().unwrap()) + .dial("/ip4/1.2.3.4/tcp/20000".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); // Success due to the DNS TXT records at _dnsaddr.bootstrap.libp2p.io. let _ = transport - .dial("/dnsaddr/bootstrap.libp2p.io".parse().unwrap()) + .dial("/dnsaddr/bootstrap.libp2p.io".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); @@ -723,7 +724,7 @@ mod tests { // an entry with suffix `/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN`, // i.e. a bootnode with such a peer ID. let _ = transport - .dial("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN".parse().unwrap()) + .dial("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); @@ -735,6 +736,7 @@ mod tests { format!("/dnsaddr/bootstrap.libp2p.io/p2p/{}", PeerId::random()) .parse() .unwrap(), + dial_opts, ) .unwrap() .await @@ -746,7 +748,10 @@ mod tests { // Failure due to no records. match transport - .dial("/dns4/example.invalid/tcp/20000".parse().unwrap()) + .dial( + "/dns4/example.invalid/tcp/20000".parse().unwrap(), + dial_opts, + ) .unwrap() .await { diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index f2656ed505d..f2c00d7d2a3 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -924,7 +924,13 @@ mod tests { let mut transport = crate::tokio::Transport::new(config); let _dial = transport - .dial("/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap()) + .dial( + "/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap(), + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap(); assert!(transport.dialer.contains_key(&SocketFamily::Ipv4)); diff --git a/transports/quic/tests/smoke.rs b/transports/quic/tests/smoke.rs index 36fb72a5ee7..a95f23748b1 100644 --- a/transports/quic/tests/smoke.rs +++ b/transports/quic/tests/smoke.rs @@ -7,8 +7,9 @@ use futures::stream::StreamExt; use futures::{future, AsyncReadExt, AsyncWriteExt, FutureExt, SinkExt}; use futures_timer::Delay; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt, SubstreamBox}; -use libp2p_core::transport::{Boxed, OrTransport, TransportEvent}; +use libp2p_core::transport::{Boxed, DialOpts, OrTransport, PortUse, TransportEvent}; use libp2p_core::transport::{ListenerId, TransportError}; +use libp2p_core::Endpoint; use libp2p_core::{multiaddr::Protocol, upgrade, Multiaddr, Transport}; use libp2p_identity::PeerId; use libp2p_noise as noise; @@ -90,6 +91,8 @@ async fn ipv4_dial_ipv6() { #[cfg(feature = "async-std")] #[async_std::test] async fn wrapped_with_delay() { + use libp2p_core::transport::DialOpts; + let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); @@ -125,7 +128,11 @@ async fn wrapped_with_delay() { /// Delayed dial, i.e. calling [`Transport::dial`] on the inner [`Transport`] not within the /// synchronous [`Transport::dial`] method, but within the [`Future`] returned by the outer /// [`Transport::dial`]. - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { let t = self.0.clone(); Ok(async move { // Simulate DNS lookup. Giving the `Transport::poll` the chance to return @@ -133,24 +140,21 @@ async fn wrapped_with_delay() { // on the inner transport below. Delay::new(Duration::from_millis(100)).await; - let dial = t.lock().unwrap().dial(addr).map_err(|e| match e { - TransportError::MultiaddrNotSupported(_) => { - panic!() - } - TransportError::Other(e) => e, - })?; + let dial = t + .lock() + .unwrap() + .dial(addr, dial_opts) + .map_err(|e| match e { + TransportError::MultiaddrNotSupported(_) => { + panic!() + } + TransportError::Other(e) => e, + })?; dial.await } .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.0.lock().unwrap().dial_as_listener(addr) - } - fn poll( self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -183,7 +187,15 @@ async fn wrapped_with_delay() { // Note that the dial is spawned on a different task than the transport allowing the transport // task to poll the transport once and then suspend, waiting for the wakeup from the dial. let dial = async_std::task::spawn({ - let dial = b_transport.dial(a_addr).unwrap(); + let dial = b_transport + .dial( + a_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(); async { dial.await.unwrap().0 } }); async_std::task::spawn(async move { b_transport.next().await }); @@ -315,7 +327,13 @@ async fn draft_29_support() { let (_, mut c_transport) = create_transport::(|cfg| cfg.support_draft_29 = false); assert!(matches!( - c_transport.dial(a_quic_addr), + c_transport.dial( + a_quic_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New + } + ), Err(TransportError::MultiaddrNotSupported(_)) )); @@ -331,7 +349,15 @@ async fn draft_29_support() { )); let d_quic_v1_addr = start_listening(&mut d_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await; let d_quic_addr_mapped = swap_protocol!(d_quic_v1_addr, QuicV1 => Quic); - let dial = b_transport.dial(d_quic_addr_mapped).unwrap(); + let dial = b_transport + .dial( + d_quic_addr_mapped, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(); let drive_transports = poll_fn::<(), _>(|cx| { let _ = b_transport.poll_next_unpin(cx); let _ = d_transport.poll_next_unpin(cx); @@ -765,7 +791,20 @@ async fn dial( transport: &mut Boxed<(PeerId, StreamMuxerBox)>, addr: Multiaddr, ) -> io::Result<(PeerId, StreamMuxerBox)> { - match future::select(transport.dial(addr).unwrap(), transport.next()).await { + match future::select( + transport + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(), + transport.next(), + ) + .await + { Either::Left((conn, _)) => conn, Either::Right((event, _)) => { panic!("Unexpected event: {event:?}") diff --git a/transports/quic/tests/stream_compliance.rs b/transports/quic/tests/stream_compliance.rs index 0eff0584588..b0536473215 100644 --- a/transports/quic/tests/stream_compliance.rs +++ b/transports/quic/tests/stream_compliance.rs @@ -1,7 +1,7 @@ use futures::channel::oneshot; use futures::StreamExt; -use libp2p_core::transport::ListenerId; -use libp2p_core::Transport; +use libp2p_core::transport::{DialOpts, ListenerId, PortUse}; +use libp2p_core::{Endpoint, Transport}; use libp2p_quic as quic; use std::time::Duration; @@ -47,7 +47,15 @@ async fn connected_peers() -> (quic::Connection, quic::Connection) { listener.next().await; } }); - let dial_fut = dialer.dial(listen_address).unwrap(); + let dial_fut = dialer + .dial( + listen_address, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(); async_std::task::spawn(async move { let connection = dial_fut.await.unwrap().1; diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index fec7b0802c5..dc4f75f9011 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -1092,7 +1092,6 @@ mod tests { #[test] fn port_reuse_listening() { - env_logger::try_init().ok(); let _ = tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .try_init(); diff --git a/transports/uds/src/lib.rs b/transports/uds/src/lib.rs index d8acabd008a..90d0c8ef135 100644 --- a/transports/uds/src/lib.rs +++ b/transports/uds/src/lib.rs @@ -46,7 +46,7 @@ use futures::{ use libp2p_core::transport::ListenerId; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{TransportError, TransportEvent, DialOpts}, + transport::{DialOpts, TransportError, TransportEvent}, Transport, }; use std::collections::VecDeque; @@ -253,8 +253,8 @@ mod tests { use futures::{channel::oneshot, prelude::*}; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::ListenerId, - Transport, + transport::{DialOpts, ListenerId, PortUse}, + Endpoint, Transport, }; use std::{self, borrow::Cow, path::Path}; @@ -311,7 +311,17 @@ mod tests { async_std::task::block_on(async move { let mut uds = UdsConfig::new(); let addr = rx.await.unwrap(); - let mut socket = uds.dial(addr).unwrap().await.unwrap(); + let mut socket = uds + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); let _ = socket.write(&[1, 2, 3]).await.unwrap(); }); } diff --git a/transports/webrtc/tests/smoke.rs b/transports/webrtc/tests/smoke.rs index 76e168edfd6..d606d66c41f 100644 --- a/transports/webrtc/tests/smoke.rs +++ b/transports/webrtc/tests/smoke.rs @@ -23,8 +23,8 @@ use futures::future::{BoxFuture, Either}; use futures::stream::StreamExt; use futures::{future, ready, AsyncReadExt, AsyncWriteExt, FutureExt, SinkExt}; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt}; -use libp2p_core::transport::{Boxed, ListenerId, TransportEvent}; -use libp2p_core::{Multiaddr, Transport}; +use libp2p_core::transport::{Boxed, DialOpts, ListenerId, PortUse, TransportEvent}; +use libp2p_core::{Endpoint, Multiaddr, Transport}; use libp2p_identity::PeerId; use libp2p_webrtc as webrtc; use rand::{thread_rng, RngCore}; @@ -322,7 +322,17 @@ struct Dial<'a> { impl<'a> Dial<'a> { fn new(dialer: &'a mut Boxed<(PeerId, StreamMuxerBox)>, addr: Multiaddr) -> Self { Self { - dial_task: dialer.dial(addr).unwrap().map(|r| r.unwrap()).boxed(), + dial_task: dialer + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .map(|r| r.unwrap()) + .boxed(), dialer, } } diff --git a/transports/websocket/src/lib.rs b/transports/websocket/src/lib.rs index 3ead3184a5a..639cab71d31 100644 --- a/transports/websocket/src/lib.rs +++ b/transports/websocket/src/lib.rs @@ -289,7 +289,11 @@ where mod tests { use super::WsConfig; use futures::prelude::*; - use libp2p_core::{multiaddr::Protocol, transport::ListenerId, Multiaddr, Transport}; + use libp2p_core::{ + multiaddr::Protocol, + transport::{DialOpts, ListenerId, PortUse}, + Endpoint, Multiaddr, Transport, + }; use libp2p_identity::PeerId; use libp2p_tcp as tcp; @@ -336,7 +340,13 @@ mod tests { let outbound = new_ws_config() .boxed() - .dial(addr.with(Protocol::P2p(PeerId::random()))) + .dial( + addr.with(Protocol::P2p(PeerId::random())), + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap(); let (a, b) = futures::join!(inbound, outbound); From a09e26d71999a0b0a27f8411f7ebd7fa5a6a091a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:13:59 +0100 Subject: [PATCH 70/97] Implement the latest changes to the protocol --- .../src/v2/client/handler/dial_back.rs | 13 ++-- .../autonat/src/v2/generated/structs.proto | 8 +++ protocols/autonat/src/v2/generated/structs.rs | 66 +++++++++++++++++++ protocols/autonat/src/v2/protocol.rs | 46 +++++++++++-- .../src/v2/server/handler/dial_back.rs | 22 +++++-- protocols/autonat/tests/autonatv2.rs | 4 +- 6 files changed, 140 insertions(+), 19 deletions(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 18bfee792f1..052fe391e39 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -4,6 +4,7 @@ use std::{ time::Duration, }; +use futures::{AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -75,11 +76,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { protocol, .. }) => { - if self - .inbound - .try_push(protocol::recv_dial_back(protocol)) - .is_err() - { + if self.inbound.try_push(perform_dial_back(protocol)).is_err() { tracing::warn!("Dial back request dropped, too many requests in flight"); } } @@ -90,3 +87,9 @@ impl ConnectionHandler for Handler { } } } + +async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { + let nonce = protocol::recv_dial_back(&mut stream).await?; + protocol::dial_back_response(stream).await?; + Ok(nonce) +} diff --git a/protocols/autonat/src/v2/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto index 7088e888052..31791463956 100644 --- a/protocols/autonat/src/v2/generated/structs.proto +++ b/protocols/autonat/src/v2/generated/structs.proto @@ -44,3 +44,11 @@ message DialResponse { message DialDataResponse { bytes data = 1; } message DialBack { fixed64 nonce = 1; } + +message DialBackResponse { + enum DialBackStatus { + OK = 0; + } + + DialBackStatus status = 1; +} diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs index 2bb613458e8..e3388012ce7 100644 --- a/protocols/autonat/src/v2/generated/structs.rs +++ b/protocols/autonat/src/v2/generated/structs.rs @@ -336,3 +336,69 @@ impl MessageWrite for DialBack { Ok(()) } } + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialBackResponse { + pub(crate) status: Option, +} + +impl<'a> MessageRead<'a> for DialBackResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialBackResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + Ok(()) + } +} + +pub(crate) mod mod_DialBackResponse { + + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum DialBackStatus { + OK = 0, +} + +impl Default for DialBackStatus { + fn default() -> Self { + DialBackStatus::OK + } +} + +impl From for DialBackStatus { + fn from(i: i32) -> Self { + match i { + 0 => DialBackStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for DialBackStatus { + fn from(s: &'a str) -> Self { + match s { + "OK" => DialBackStatus::OK, + _ => Self::default(), + } + } +} + +} diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index f6776093868..5c211b353c8 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, io}; use asynchronous_codec::{Framed, FramedRead, FramedWrite}; -use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; @@ -273,14 +273,11 @@ pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> .send(msg) .await .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - framed.close().await?; Ok(()) } -pub(crate) async fn recv_dial_back( - stream: impl AsyncRead + AsyncWrite + Unpin, -) -> io::Result { +pub(crate) async fn recv_dial_back(stream: impl AsyncRead + Unpin) -> io::Result { let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); let proto::DialBack { nonce } = framed .next() @@ -291,6 +288,45 @@ pub(crate) async fn recv_dial_back( Ok(nonce) } +pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::Result<()> { + let msg = proto::DialBackResponse { + status: Some(proto::mod_DialBackResponse::DialBackStatus::OK), + }; + let mut framed = FramedWrite::new( + stream, + Codec::::new(DIAL_BACK_MAX_SIZE), + ); + framed + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + framed.close().await?; + + Ok(()) +} + +pub(crate) async fn recv_dial_back_response( + stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result<()> { + let framed = &mut FramedRead::new( + stream, + Codec::::new(DIAL_BACK_MAX_SIZE), + ); + let proto::DialBackResponse { status } = framed + .next() + .await + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; + framed.close().await?; + if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid dial back response", + )) + } +} + #[cfg(test)] mod tests { use crate::v2::generated::structs::{ diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index 2aa87fd1294..3cacd4ff32b 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use futures::AsyncWrite; +use futures::{AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -14,7 +14,10 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL}; +use crate::v2::{ + protocol::{dial_back, recv_dial_back_response}, + DIAL_BACK_PROTOCOL, +}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -111,20 +114,25 @@ impl ConnectionHandler for Handler { } async fn perform_dial_back( - stream: impl AsyncWrite + Unpin, + mut stream: impl AsyncRead + AsyncWrite + Unpin, DialBackCommand { nonce, back_channel, .. }: DialBackCommand, ) -> io::Result<()> { - let res = dial_back(stream, nonce) + let res = dial_back(&mut stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| ()); - // this exists to prevent a synchronization issue on the client side. Whitout this, the client - // might already receive a - futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; + + let res = match res { + Ok(()) => recv_dial_back_response(stream) + .await + .map_err(|_| DialBackRes::DialBackErr) + .map(|_| ()), + Err(e) => Err(e), + }; back_channel .send(res) .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 150de669362..237be45deaf 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -238,7 +238,7 @@ async fn dial_back_to_non_libp2p() { for addr_str in [ "/ip4/169.150.247.38/tcp/32", - "/ip6/2400:52e0:1e02::1187:1/tcp/1000", + "/ip6/::1/tcp/1000", ] { let addr: Multiaddr = addr_str.parse().unwrap(); let bob_addr = addr.clone(); @@ -279,7 +279,7 @@ async fn dial_back_to_non_libp2p() { let error_string = o.to_string(); assert!( error_string.contains("Connection refused"), - "Coorect error string: {error_string}" + "Correct error string: {error_string} for {addr_str}" ); } else { panic!("No outgoing connection errors"); From 61ae32ef95636ef20ef4e6a495eaf3bba7b20ae5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:27:38 +0100 Subject: [PATCH 71/97] Fix minor nits --- transports/quic/src/transport.rs | 2 +- transports/webrtc/src/tokio/transport.rs | 14 +++++++++----- wasm-tests/webtransport-tests/src/lib.rs | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index f2c00d7d2a3..66093874e19 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -267,7 +267,7 @@ impl Transport for GenTransport

{ self.remote_multiaddr_to_socketaddr(addr.clone(), true)?; match (dial_opts.role, dial_opts.port_use) { - (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::New) => { + (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::Reuse) => { let endpoint = match self.eligible_listener(&socket_addr) { None => { // No listener. Get or create an explicit dialer. diff --git a/transports/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index 70e8e10b9d8..b30ac278728 100644 --- a/transports/webrtc/src/tokio/transport.rs +++ b/transports/webrtc/src/tokio/transport.rs @@ -121,12 +121,16 @@ impl libp2p_core::Transport for Transport { fn dial( &mut self, addr: Multiaddr, - _: DialOpts, + dial_opts: DialOpts, ) -> Result> { - // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the - // `addr`. See DCUtR specification below. - // - // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol + if dial_opts.role.is_listener() { + // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the + // `addr`. See DCUtR specification below. + // + // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol + tracing::warn!("WebRTC hole punch is not yet supported"); + } + let (sock_addr, server_fingerprint) = libp2p_webrtc_utils::parse_webrtc_dial_addr(&addr) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if sock_addr.port() == 0 || sock_addr.ip().is_unspecified() { diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index ad160f19d91..b4d795fa5fe 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -268,8 +268,8 @@ async fn connect_without_peer_id() { .dial( addr, DialOpts { - role: Endpoint::Dialer, - port_use: PortUse::Reuse, + role: Endpoint::Listener, + port_use: PortUse::New, }, ) .unwrap() From ad6ad8fbfa01a02bb7a2892273c4dd3e67d089f5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:54:33 +0100 Subject: [PATCH 72/97] Implement DialBackOk correctly --- protocols/autonat/src/v2/client/behaviour.rs | 15 +++- .../src/v2/client/handler/dial_back.rs | 76 ++++++++++++++----- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e257a4b833d..f0648808c37 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -19,7 +19,10 @@ use std::fmt::{Debug, Display, Formatter}; use crate::v2::{protocol::DialRequest, Nonce}; -use super::handler::{dial_back, dial_request}; +use super::handler::{ + dial_back::{self, IncomingNonce}, + dial_request, +}; #[derive(Debug, Clone, Copy)] pub struct Config { @@ -148,19 +151,27 @@ where event: ::ToBehaviour, ) { let (nonce, outcome) = match event { - Either::Right(nonce) => { + Either::Right(IncomingNonce { nonce, sender }) => { let Some((_, info)) = self .address_candidates .iter_mut() .find(|(_, info)| info.is_pending_with_nonce(nonce)) else { tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); + let _ = sender.send(Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Received unexpected nonce", + ))); return; }; info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + if let Err(e) = sender.send(Ok(())) { + tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e:?}"); + } + return; } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 052fe391e39..d395e0cf925 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -4,8 +4,8 @@ use std::{ time::Duration, }; -use futures::{AsyncRead, AsyncWrite}; -use futures_bounded::FuturesSet; +use futures::{channel::oneshot, AsyncWriteExt}; +use futures_bounded::StreamSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, @@ -16,20 +16,20 @@ use void::Void; use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; pub struct Handler { - inbound: FuturesSet>, + inbound: StreamSet>, } impl Handler { pub(crate) fn new() -> Self { Self { - inbound: FuturesSet::new(Duration::from_secs(5), 2), + inbound: StreamSet::new(Duration::from_secs(5), 2), } } } impl ConnectionHandler for Handler { type FromBehaviour = Void; - type ToBehaviour = Nonce; + type ToBehaviour = IncomingNonce; type InboundProtocol = ReadyUpgrade; type OutboundProtocol = DeniedUpgrade; type InboundOpenInfo = (); @@ -45,19 +45,20 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(Ok(nonce))) => { - Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) + match self.inbound.poll_next_unpin(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Pending, + Poll::Ready(Some(Err(err))) => { + tracing::debug!("Dial back handler timed out with: {err:?}"); + Poll::Pending } - Poll::Ready(Ok(Err(err))) => { + Poll::Ready(Some(Ok(Err(err)))) => { tracing::debug!("Dial back handler failed with: {err:?}"); Poll::Pending } - Poll::Ready(Err(err)) => { - tracing::debug!("Dial back handler timed out with: {err:?}"); - Poll::Pending + Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)) } - Poll::Pending => Poll::Pending, } } @@ -88,8 +89,49 @@ impl ConnectionHandler for Handler { } } -async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { - let nonce = protocol::recv_dial_back(&mut stream).await?; - protocol::dial_back_response(stream).await?; - Ok(nonce) +struct State { + stream: libp2p_swarm::Stream, + oneshot: Option>>, +} + +#[derive(Debug)] +pub struct IncomingNonce { + pub nonce: Nonce, + pub sender: oneshot::Sender>, +} + +fn perform_dial_back( + stream: libp2p_swarm::Stream, +) -> impl futures::Stream> { + let state = State { + stream, + oneshot: None, + }; + futures::stream::unfold(state, |mut state| async move { + if let Some(ref mut receiver) = state.oneshot { + if receiver.await.is_err() { + return Some((Err(io::Error::from(io::ErrorKind::Other)), state)); + } + if let Err(e) = protocol::dial_back_response(&mut state.stream).await { + let _ = state.stream.close().await; + return Some((Err(e), state)); + } + if let Err(e) = state.stream.close().await { + return Some((Err(e), state)); + } + return None; + } + + let nonce = match protocol::recv_dial_back(&mut state.stream).await { + Ok(nonce) => nonce, + Err(err) => { + let _ = state.stream.close().await; + return Some((Err(err), state)); + } + }; + + let (sender, receiver) = oneshot::channel(); + state.oneshot = Some(receiver); + Some((Ok(IncomingNonce { nonce, sender }), state)) + }) } From fbdc85fc85db6a46267a56b3f4805319c062d0e8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:57:27 +0100 Subject: [PATCH 73/97] Format --- examples/autonat/src/bin/autonat_client.rs | 1 - examples/autonat/src/bin/autonat_server.rs | 1 - examples/autonatv2/src/lib.rs | 1 + misc/server/src/behaviour.rs | 1 - protocols/autonat/tests/autonatv2.rs | 5 +---- protocols/rendezvous/src/server.rs | 9 +++++++-- protocols/upnp/src/behaviour.rs | 6 +++++- 7 files changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 42b3995b57b..6ef8b90f448 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -19,7 +19,6 @@ // DEALINGS IN THE SOFTWARE. #![doc = include_str!("../../README.md")] - #![allow(deprecated)] use clap::Parser; diff --git a/examples/autonat/src/bin/autonat_server.rs b/examples/autonat/src/bin/autonat_server.rs index 7545b8dac06..4a1a3c18401 100644 --- a/examples/autonat/src/bin/autonat_server.rs +++ b/examples/autonat/src/bin/autonat_server.rs @@ -19,7 +19,6 @@ // DEALINGS IN THE SOFTWARE. #![doc = include_str!("../../README.md")] - #![allow(deprecated)] use clap::Parser; diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs index e69de29bb2d..8b137891791 100644 --- a/examples/autonatv2/src/lib.rs +++ b/examples/autonatv2/src/lib.rs @@ -0,0 +1 @@ + diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index 811b103c9c7..dd5a24b465e 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -10,7 +10,6 @@ use libp2p::{identity, swarm::NetworkBehaviour, Multiaddr, PeerId}; use std::str::FromStr; use std::time::Duration; - const BOOTNODES: [&str; 4] = [ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 237be45deaf..cd80cfa9a26 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -236,10 +236,7 @@ async fn dial_back_to_non_libp2p() { let (mut alice, mut bob) = bootstrap().await; let alice_peer_id = *alice.local_peer_id(); - for addr_str in [ - "/ip4/169.150.247.38/tcp/32", - "/ip6/::1/tcp/1000", - ] { + for addr_str in ["/ip4/169.150.247.38/tcp/32", "/ip6/::1/tcp/1000"] { let addr: Multiaddr = addr_str.parse().unwrap(); let bob_addr = addr.clone(); bob.behaviour_mut() diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 60dc067cbba..92ff95650ba 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -143,8 +143,13 @@ impl NetworkBehaviour for Behaviour { role_override: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_connection_handler_event( diff --git a/protocols/upnp/src/behaviour.rs b/protocols/upnp/src/behaviour.rs index 70d6afbffcc..29a7fbf84a4 100644 --- a/protocols/upnp/src/behaviour.rs +++ b/protocols/upnp/src/behaviour.rs @@ -36,7 +36,11 @@ use crate::tokio::{is_addr_global, Gateway}; use futures::{channel::oneshot, Future, StreamExt}; use futures_timer::Delay; use igd_next::PortMappingProtocol; -use libp2p_core::{multiaddr, transport::{ListenerId, PortUse}, Endpoint, Multiaddr}; +use libp2p_core::{ + multiaddr, + transport::{ListenerId, PortUse}, + Endpoint, Multiaddr, +}; use libp2p_swarm::{ derive_prelude::PeerId, dummy, ConnectionDenied, ConnectionId, ExpiredListenAddr, FromSwarm, NetworkBehaviour, NewListenAddr, ToSwarm, From 553511c4494fc4f2dffc7bac902b5d35720d3558 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:00:23 +0100 Subject: [PATCH 74/97] Fix issues raised by clippy --- .../autonat/src/v2/client/handler/dial_request.rs | 2 +- protocols/autonat/src/v2/protocol.rs | 10 +++++----- protocols/autonat/src/v2/server/behaviour.rs | 13 +++++-------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 16934c478fb..5191382734f 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -234,7 +234,7 @@ async fn start_stream_handle( "address index out of bounds", ))); } - if num_bytes > DATA_LEN_UPPER_BOUND || num_bytes < DATA_LEN_LOWER_BOUND { + if !(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND).contains(&num_bytes) { return Err(Error::Io(io::Error::new( io::ErrorKind::InvalidInput, "requested bytes out of bounds", diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 5c211b353c8..68f3c3b1cf9 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -226,10 +226,10 @@ impl TryFrom for Response { } } -impl Into for Response { - fn into(self) -> proto::Message { - match self { - Self::Dial(DialResponse { +impl From for proto::Message { + fn from(val: Response) -> Self { + match val { + Response::Dial(DialResponse { status, addr_idx, dial_status, @@ -240,7 +240,7 @@ impl Into for Response { dialStatus: Some(dial_status), }), }, - Self::Data(DialDataRequest { + Response::Data(DialDataRequest { addr_idx, num_bytes, }) => proto::Message { diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 907faeec823..97446c6263f 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -91,15 +91,12 @@ where } fn on_swarm_event(&mut self, event: FromSwarm) { - match event { - FromSwarm::DialFailure(DialFailure { connection_id, .. }) => { - if let Some(DialBackCommand { back_channel, .. }) = - self.dialing_dial_back.remove(&connection_id) - { - let _ = back_channel.send(Err(DialBackStatus::DialErr)); - } + if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { + if let Some(DialBackCommand { back_channel, .. }) = + self.dialing_dial_back.remove(&connection_id) + { + let _ = back_channel.send(Err(DialBackStatus::DialErr)); } - _ => {} } } From 9bf52d454cff5774d530745ff671578377134533 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:54:16 +0100 Subject: [PATCH 75/97] Update protocols/autonat/src/v2/client/behaviour.rs Co-authored-by: Thomas Eizinger --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index f0648808c37..cffa9b3f29b 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -169,7 +169,7 @@ where tracing::debug!(%peer_id, %nonce, "Successful dial-back"); if let Err(e) = sender.send(Ok(())) { - tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e:?}"); + tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e}"); } return; From 70f41f325b2e2a567b450755198f4fcd4e923931 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:54:34 +0100 Subject: [PATCH 76/97] Update protocols/autonat/src/v2/client/handler/dial_back.rs Co-authored-by: Thomas Eizinger --- protocols/autonat/src/v2/client/handler/dial_back.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index d395e0cf925..8fb77013d86 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -49,7 +49,7 @@ impl ConnectionHandler for Handler { Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Pending, Poll::Ready(Some(Err(err))) => { - tracing::debug!("Dial back handler timed out with: {err:?}"); + tracing::debug!("Stream timed out: {err}"); Poll::Pending } Poll::Ready(Some(Ok(Err(err)))) => { From 077369478bdf7e916b703c86cad53bb9ae281572 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:54:48 +0100 Subject: [PATCH 77/97] Update protocols/autonat/src/v2/client/handler/dial_back.rs Co-authored-by: Thomas Eizinger --- protocols/autonat/src/v2/client/handler/dial_back.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 8fb77013d86..08ef7d3576e 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -113,7 +113,6 @@ fn perform_dial_back( return Some((Err(io::Error::from(io::ErrorKind::Other)), state)); } if let Err(e) = protocol::dial_back_response(&mut state.stream).await { - let _ = state.stream.close().await; return Some((Err(e), state)); } if let Err(e) = state.stream.close().await { From 89acd28ebebb3b088a4610b712e6e394c0d893ea Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:57:55 +0100 Subject: [PATCH 78/97] Make error richer and trace it at a different point --- protocols/autonat/src/v2/client/behaviour.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index cffa9b3f29b..58f1910d6e5 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -157,10 +157,9 @@ where .iter_mut() .find(|(_, info)| info.is_pending_with_nonce(nonce)) else { - tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); let _ = sender.send(Err(std::io::Error::new( std::io::ErrorKind::InvalidData, - "Received unexpected nonce", + format!("Received unexpected nonce: {nonce} from {peer_id}"), ))); return; }; From d571e518f83c13679e87363f64ef4eda06c693be Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:59:10 +0100 Subject: [PATCH 79/97] Avoid stream close --- protocols/autonat/src/v2/client/handler/dial_back.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 08ef7d3576e..3e8bec9dbe1 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -124,7 +124,6 @@ fn perform_dial_back( let nonce = match protocol::recv_dial_back(&mut state.stream).await { Ok(nonce) => nonce, Err(err) => { - let _ = state.stream.close().await; return Some((Err(err), state)); } }; From 10e4ceefed26384ca6bcbef2ba7158fa1e28b80f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:05:57 +0100 Subject: [PATCH 80/97] Better error handling --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- protocols/autonat/src/v2/client/handler/dial_back.rs | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 58f1910d6e5..06aed2d544c 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -167,7 +167,7 @@ where info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); - if let Err(e) = sender.send(Ok(())) { + if let Err(Err(e)) = sender.send(Ok(())) { tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e}"); } diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 3e8bec9dbe1..b1923bfd023 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -109,8 +109,15 @@ fn perform_dial_back( }; futures::stream::unfold(state, |mut state| async move { if let Some(ref mut receiver) = state.oneshot { - if receiver.await.is_err() { - return Some((Err(io::Error::from(io::ErrorKind::Other)), state)); + match receiver.await { + Ok(Ok(())) => {} + Ok(Err(e)) => return Some((Err(e), state)), + Err(_) => { + return Some(( + Err(io::Error::new(io::ErrorKind::Other, "Sender got cancelled")), + state, + )); + } } if let Err(e) = protocol::dial_back_response(&mut state.stream).await { return Some((Err(e), state)); From d4d671ecaed511dce6946b09a4271d3b65f7a599 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:22:46 +0100 Subject: [PATCH 81/97] Correct one failing CI and change the license --- examples/autonat/Cargo.toml | 2 +- examples/autonatv2/Cargo.toml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/autonat/Cargo.toml b/examples/autonat/Cargo.toml index 038c1800308..761e1a67c56 100644 --- a/examples/autonat/Cargo.toml +++ b/examples/autonat/Cargo.toml @@ -3,7 +3,7 @@ name = "autonat-example" version = "0.1.0" edition = "2021" publish = false -license = "MIT" +license = "MIT or Apache-2.0" [package.metadata.release] release = false diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index 9a0c0f6ebcb..767f7a013df 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -2,10 +2,11 @@ name = "autonatv2" version = "0.1.0" edition = "2021" -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +publish = false +license = "MIT" +[package.metadata.release] +release = false [[bin]] name = "autonatv2_client" From 330fc518c309564b125bce9dc264707ca0339ac0 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:30:52 +0100 Subject: [PATCH 82/97] Correct minor things --- protocols/autonat/src/v2/client/behaviour.rs | 4 +-- .../src/v2/client/handler/dial_back.rs | 31 +++++++++---------- .../src/v2/server/handler/dial_request.rs | 29 ++++++++--------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 06aed2d544c..97509c05443 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -167,9 +167,7 @@ where info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); - if let Err(Err(e)) = sender.send(Ok(())) { - tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e}"); - } + let _ = sender.send(Ok(())); return; } diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index b1923bfd023..25a97880e6d 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -45,19 +45,21 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - match self.inbound.poll_next_unpin(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Pending, - Poll::Ready(Some(Err(err))) => { - tracing::debug!("Stream timed out: {err}"); - Poll::Pending - } - Poll::Ready(Some(Ok(Err(err)))) => { - tracing::debug!("Dial back handler failed with: {err:?}"); - Poll::Pending - } - Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { - Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)) + loop { + match self.inbound.poll_next_unpin(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => continue, + Poll::Ready(Some(Err(err))) => { + tracing::debug!("Stream timed out: {err}"); + continue; + } + Poll::Ready(Some(Ok(Err(err)))) => { + tracing::debug!("Dial back handler failed with: {err:?}"); + continue; + } + Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)); + } } } } @@ -122,9 +124,6 @@ fn perform_dial_back( if let Err(e) = protocol::dial_back_response(&mut state.stream).await { return Some((Err(e), state)); } - if let Err(e) = state.stream.close().await { - return Some((Err(e), state)); - } return None; } diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index 382df27391f..f8c3f6b654d 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -90,19 +90,21 @@ where ) -> Poll< ConnectionHandlerEvent, > { - match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(event)) => { - if let Err(e) = &event.result { - tracing::warn!("inbound request handle failed: {:?}", e); + loop { + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(event)) => { + if let Err(e) = &event.result { + tracing::warn!("inbound request handle failed: {:?}", e); + } + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + event, + ))); } - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( - event, - ))); - } - Poll::Ready(Err(e)) => { - tracing::warn!("inbound request handle timed out {e:?}"); + Poll::Ready(Err(e)) => { + tracing::warn!("inbound request handle timed out {e:?}"); + } + Poll::Pending => break, } - Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); @@ -212,7 +214,7 @@ async fn handle_request( ) .await .unwrap_or_else(|e| e.into()); - if tested_addr_opt.is_none() { + let Some(tested_addr) = tested_addr_opt else { return Event { all_addrs, tested_addr: observed_multiaddr, @@ -223,8 +225,7 @@ async fn handle_request( "client is not conformint to protocol. the tested address is not the observed address", )), }; - } - let tested_addr = tested_addr_opt.unwrap(); + }; if let Err(e) = coder.send(Response::Dial(response)).await { return Event { all_addrs, From 367273a6e43789fdcc777b275a3f94913706f160 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:05:21 +0100 Subject: [PATCH 83/97] Add quic and dns to the example --- examples/autonatv2/Cargo.toml | 3 +-- examples/autonatv2/src/bin/autonatv2_client.rs | 2 ++ examples/autonatv2/src/bin/autonatv2_server.rs | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index 767f7a013df..7fd9b41f05c 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -15,8 +15,7 @@ name = "autonatv2_client" name = "autonatv2_server" [dependencies] -libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", - "identify"]} +libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", "identify", "dns", "quic"] } clap = { version = "4.4.18", features = ["derive"] } tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } tracing = "0.1.40" diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs index 8f9ce9e5eee..de902514dd8 100644 --- a/examples/autonatv2/src/bin/autonatv2_client.rs +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -44,6 +44,8 @@ async fn main() -> Result<(), Box> { noise::Config::new, yamux::Config::default, )? + .with_quic() + .with_dns()? .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) .build(); diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs index cf1798031ec..849ed3b3b0a 100644 --- a/examples/autonatv2/src/bin/autonatv2_server.rs +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -50,6 +50,8 @@ async fn main() -> Result<(), Box> { noise::Config::new, yamux::Config::default, )? + .with_quic() + .with_dns()? .with_behaviour(|key| Behaviour::new(key.public()))? .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) .build(); From d79bca3b3ea4c374d0b0cbb9129478a8639fa807 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:22:02 +0100 Subject: [PATCH 84/97] Remove unused import --- protocols/autonat/src/v2/client/handler/dial_back.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 25a97880e6d..b94580e69ba 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use futures::{channel::oneshot, AsyncWriteExt}; +use futures::channel::oneshot; use futures_bounded::StreamSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ From 8df0183e206e3581d8168fffe016bcd5c9847dd0 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:15:34 +0100 Subject: [PATCH 85/97] Change dial opts to make wasm tests pass --- wasm-tests/webtransport-tests/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index b4d795fa5fe..75d7112e02d 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -268,8 +268,8 @@ async fn connect_without_peer_id() { .dial( addr, DialOpts { - role: Endpoint::Listener, - port_use: PortUse::New, + role: Endpoint::Dialer, + port_use: PortUse::Reuse, }, ) .unwrap() @@ -322,8 +322,8 @@ async fn error_on_unknown_certhash() { .dial( addr.clone(), DialOpts { - role: Endpoint::Listener, - port_use: PortUse::New, + role: Endpoint::Dialer, + port_use: PortUse::Reuse, }, ) .unwrap() @@ -346,7 +346,7 @@ async fn new_connection_to_echo_server() -> Connection { addr, DialOpts { role: Endpoint::Dialer, - port_use: PortUse::New, + port_use: PortUse::Reuse, }, ) .unwrap() From 5d24d03ff6878b67b25b29811f7298f2a71e8522 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:22:06 +0100 Subject: [PATCH 86/97] Remove futures-time to compile on wasm --- Cargo.lock | 13 ------------- protocols/autonat/Cargo.toml | 1 - 2 files changed, 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 401b6114aa6..cc710b88a4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1707,18 +1707,6 @@ dependencies = [ "instant", ] -[[package]] -name = "futures-time" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6404853a6824881fe5f7d662d147dc4e84ecd2259ba0378f272a71dab600758a" -dependencies = [ - "async-channel", - "async-io 1.13.0", - "futures-core", - "pin-project-lite", -] - [[package]] name = "futures-timer" version = "3.0.3" @@ -2628,7 +2616,6 @@ dependencies = [ "either", "futures", "futures-bounded", - "futures-time", "futures-timer", "instant", "libp2p-core", diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 3a79215e2cf..b20fb4c596c 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -18,7 +18,6 @@ bytes = { version = "1", optional = true } either = { version = "1.9.0", optional = true } futures = "0.3" futures-bounded = { workspace = true, optional = true } -futures-time = "3" futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } From 41804be4ebfbffc8d976e2e2e320b94a74cc74c3 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:43:48 +0100 Subject: [PATCH 87/97] Fix concerns by clippy --- muxers/mplex/benches/split_send_size.rs | 13 +++++- protocols/autonat/tests/autonatv2.rs | 61 ++++++++++++------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/muxers/mplex/benches/split_send_size.rs b/muxers/mplex/benches/split_send_size.rs index 0125d49dcef..44eafa884ac 100644 --- a/muxers/mplex/benches/split_send_size.rs +++ b/muxers/mplex/benches/split_send_size.rs @@ -28,6 +28,7 @@ use futures::prelude::*; use futures::{channel::oneshot, future::join}; use libp2p_core::muxing::StreamMuxerExt; use libp2p_core::transport::ListenerId; +use libp2p_core::Endpoint; use libp2p_core::{multiaddr::multiaddr, muxing, transport, upgrade, Multiaddr, Transport}; use libp2p_identity as identity; use libp2p_identity::PeerId; @@ -146,7 +147,17 @@ fn run( // Spawn and block on the sender, i.e. until all data is sent. let sender = async move { let addr = addr_receiver.await.unwrap(); - let (_peer, mut conn) = sender_trans.dial(addr).unwrap().await.unwrap(); + let (_peer, mut conn) = sender_trans + .dial( + addr, + transport::DialOpts { + role: Endpoint::Dialer, + port_use: transport::PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); // Just calling `poll_outbound` without `poll` is fine here because mplex makes progress through all `poll_` functions. It is hacky though. let mut stream = poll_fn(|cx| conn.poll_outbound_unpin(cx)).await.unwrap(); let mut off = 0; diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index cd80cfa9a26..4b070297c94 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -19,8 +19,8 @@ async fn confirm_successful() { .try_init(); let (mut alice, mut bob) = start_and_connect().await; - let cor_server_peer = alice.local_peer_id().clone(); - let cor_client_peer = bob.local_peer_id().clone(); + let cor_server_peer = *alice.local_peer_id(); + let cor_client_peer = *bob.local_peer_id(); let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); let alice_bob_external_addrs = bob_external_addrs.clone(); @@ -76,7 +76,7 @@ async fn confirm_successful() { }) .await; - assert_eq!(tested_addr, bob_external_addrs.get(0).cloned().unwrap()); + assert_eq!(tested_addr, bob_external_addrs.first().cloned().unwrap()); assert_eq!(data_amount, 0); assert_eq!(client, cor_client_peer); assert_eq!(&all_addrs[..], &bob_external_addrs[..]); @@ -96,14 +96,16 @@ async fn confirm_successful() { }) .await; - let _ = bob.wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }); + let _ = bob + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }) + .await; let client::Event { tested_addr, @@ -120,7 +122,7 @@ async fn confirm_successful() { .await; assert_eq!( tested_addr, - alice_bob_external_addrs.get(0).cloned().unwrap() + alice_bob_external_addrs.first().cloned().unwrap() ); assert_eq!(bytes_sent, 0); assert_eq!(server, cor_server_peer); @@ -282,7 +284,7 @@ async fn dial_back_to_non_libp2p() { panic!("No outgoing connection errors"); } - let data_amount = alice + alice .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, @@ -298,22 +300,19 @@ async fn dial_back_to_non_libp2p() { } _ => None, }) - .await; - data_amount + .await }; let bob_task = async { - let data_amount = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), - _ => None, - }) - .await; - data_amount + bob.wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { + tested_addr, + bytes_sent, + server, + result: Err(_), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), + _ => None, + }) + .await }; let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); @@ -392,7 +391,7 @@ async fn dial_back_to_not_supporting() { .await; tokio::select! { _ = bob_done_rx => { - return data_amount; + data_amount } _ = alice.loop_on_next() => { unreachable!(); @@ -487,8 +486,8 @@ async fn start_and_connect() -> (Swarm, Swarm) { async fn bootstrap() -> (Swarm, Swarm) { let (mut alice, mut bob) = start_and_connect().await; - let cor_server_peer = alice.local_peer_id().clone(); - let cor_client_peer = bob.local_peer_id().clone(); + let cor_server_peer = *alice.local_peer_id(); + let cor_client_peer = *bob.local_peer_id(); let alice_task = async { let _ = alice @@ -553,7 +552,7 @@ async fn bootstrap() -> (Swarm, Swarm) { .. } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), _ => None, - }); + }).await; bob.wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), From bd495f925288b889b770c924d8b55f09ac6a5c9f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:44:52 +0100 Subject: [PATCH 88/97] Format --- protocols/autonat/tests/autonatv2.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 4b070297c94..abd0c4bd8eb 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -545,14 +545,16 @@ async fn bootstrap() -> (Swarm, Swarm) { }) .await; - let _ = bob.wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }).await; + let _ = bob + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }) + .await; bob.wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), From 38be2b457862714d021f569d352133ad897f0b8a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:00:07 +0100 Subject: [PATCH 89/97] Use workaround to make generated code CI pass --- protocols/autonat/src/v2.rs | 6 +- protocols/autonat/src/v2/generated/mod.rs | 2 +- protocols/autonat/src/v2/generated/structs.rs | 55 +++++++++---------- protocols/autonat/src/v2/protocol.rs | 4 +- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index d95d51b79e9..8cab10a1d14 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -1,10 +1,14 @@ use libp2p_swarm::StreamProtocol; pub mod client; -mod generated; pub(crate) mod protocol; pub mod server; +pub(crate) mod generated { + #![allow(unreachable_pub)] + include!("v2/generated/mod.rs"); +} + pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = diff --git a/protocols/autonat/src/v2/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs index 74078a88020..e52c5a80bc0 100644 --- a/protocols/autonat/src/v2/generated/mod.rs +++ b/protocols/autonat/src/v2/generated/mod.rs @@ -1,2 +1,2 @@ // Automatically generated mod.rs -pub(crate) mod structs; +pub mod structs; diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs index e3388012ce7..12568dd0364 100644 --- a/protocols/autonat/src/v2/generated/structs.rs +++ b/protocols/autonat/src/v2/generated/structs.rs @@ -9,14 +9,12 @@ #![cfg_attr(rustfmt, rustfmt_skip)] -use std::borrow::Cow; - use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum DialStatus { +pub enum DialStatus { UNUSED = 0, E_DIAL_ERROR = 100, E_DIAL_BACK_ERROR = 101, @@ -55,8 +53,8 @@ impl<'a> From<&'a str> for DialStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct Message { - pub(crate) msg: structs::mod_Message::OneOfmsg, +pub struct Message { + pub msg: structs::mod_Message::OneOfmsg, } impl<'a> MessageRead<'a> for Message { @@ -97,12 +95,12 @@ impl MessageWrite for Message { } } -pub(crate) mod mod_Message { +pub mod mod_Message { use super::*; #[derive(Debug, PartialEq, Clone)] -pub(crate) enum OneOfmsg { +pub enum OneOfmsg { dialRequest(structs::DialRequest), dialResponse(structs::DialResponse), dialDataRequest(structs::DialDataRequest), @@ -120,9 +118,9 @@ impl Default for OneOfmsg { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialRequest { - pub(crate) addrs: Vec>, - pub(crate) nonce: Option, +pub struct DialRequest { + pub addrs: Vec>, + pub nonce: Option, } impl<'a> MessageRead<'a> for DialRequest { @@ -156,9 +154,9 @@ impl MessageWrite for DialRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialDataRequest { - pub(crate) addrIdx: Option, - pub(crate) numBytes: Option, +pub struct DialDataRequest { + pub addrIdx: Option, + pub numBytes: Option, } impl<'a> MessageRead<'a> for DialDataRequest { @@ -192,10 +190,10 @@ impl MessageWrite for DialDataRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialResponse { - pub(crate) status: Option, - pub(crate) addrIdx: Option, - pub(crate) dialStatus: Option, +pub struct DialResponse { + pub status: Option, + pub addrIdx: Option, + pub dialStatus: Option, } impl<'a> MessageRead<'a> for DialResponse { @@ -230,11 +228,11 @@ impl MessageWrite for DialResponse { } } -pub(crate) mod mod_DialResponse { +pub mod mod_DialResponse { #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum ResponseStatus { +pub enum ResponseStatus { E_INTERNAL_ERROR = 0, E_REQUEST_REJECTED = 100, E_DIAL_REFUSED = 101, @@ -275,8 +273,8 @@ impl<'a> From<&'a str> for ResponseStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialDataResponse { - pub(crate) data: Option>, +pub struct DialDataResponse { + pub data: Option>, } impl<'a> MessageRead<'a> for DialDataResponse { @@ -284,7 +282,7 @@ impl<'a> MessageRead<'a> for DialDataResponse { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned().into()), + Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned()), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -307,8 +305,8 @@ impl MessageWrite for DialDataResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialBack { - pub(crate) nonce: Option, +pub struct DialBack { + pub nonce: Option, } impl<'a> MessageRead<'a> for DialBack { @@ -339,8 +337,8 @@ impl MessageWrite for DialBack { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialBackResponse { - pub(crate) status: Option, +pub struct DialBackResponse { + pub status: Option, } impl<'a> MessageRead<'a> for DialBackResponse { @@ -369,11 +367,11 @@ impl MessageWrite for DialBackResponse { } } -pub(crate) mod mod_DialBackResponse { +pub mod mod_DialBackResponse { #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum DialBackStatus { +pub enum DialBackStatus { OK = 0, } @@ -402,3 +400,4 @@ impl<'a> From<&'a str> for DialBackStatus { } } + diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 68f3c3b1cf9..4cfb17df057 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -1,7 +1,7 @@ // change to quick-protobuf-codec +use std::io; use std::io::ErrorKind; -use std::{borrow::Cow, io}; use asynchronous_codec::{Framed, FramedRead, FramedWrite}; @@ -110,7 +110,7 @@ impl From for proto::Message { static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; proto::Message { msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(Cow::Borrowed(&DATA[..val.data_count])), + data: Some(DATA[..val.data_count].to_vec()), // Once could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI }), } } From ce7596ccbf3051f79f8aafd73eabe206e1f565ae Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:05:30 +0100 Subject: [PATCH 90/97] Correct webtransport test --- wasm-tests/webtransport-tests/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index 75d7112e02d..938cdf0b3e1 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -293,8 +293,8 @@ async fn error_on_unknown_peer_id() { .dial( addr.clone(), DialOpts { - role: Endpoint::Listener, - port_use: PortUse::New, + role: Endpoint::Dialer, + port_use: PortUse::Reuse, }, ) .unwrap() From 0506c7b07c6be5a1902c894d7d857522412ddec8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:10:50 +0100 Subject: [PATCH 91/97] Handle strange variable unused on windows --- transports/tcp/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index dc4f75f9011..228cdbd7a30 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -280,6 +280,10 @@ where if port_use == PortUse::Reuse { socket.set_reuse_port(true)?; } + + #[cfg(not(all(unix, not(any(target_os = "solaris", target_os = "illumos")))))] + let _ = port_use; // silence the unused warning on non-unix platforms (i.e. Windows) + Ok(socket) } From 0b6dbb1aa685c7eb70829404de062914e7b50cfb Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:11:45 +0100 Subject: [PATCH 92/97] Remove clippy nit --- protocols/autonat/src/v2/protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 4cfb17df057..6cd6bb61e39 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -337,7 +337,7 @@ mod tests { fn message_correct_max_size() { let message_bytes = quick_protobuf::serialize_into_vec(&Message { msg: OneOfmsg::dialDataResponse(GenDialDataResponse { - data: Some(vec![0; 4096].into()), + data: Some(vec![0; 4096]), }), }) .unwrap(); From d2fddd5a27d4078c496c8330c672079eaec61f5e Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 22:04:47 +0100 Subject: [PATCH 93/97] Simplify a little bit and get rid of every performance opt --- protocols/autonat/src/v2/protocol.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 6cd6bb61e39..d0e2fc30ca6 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -107,10 +107,9 @@ impl From for proto::Message { val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, "data_count too large" ); - static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; proto::Message { msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(DATA[..val.data_count].to_vec()), // Once could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI + data: Some(vec![0; val.data_count]), // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI }), } } From 8f1fcbb57f9b00d89e47cf6e2f717d77f64874af Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:37:42 +0100 Subject: [PATCH 94/97] Version bumps and added author --- Cargo.lock | 2 +- Cargo.toml | 2 +- core/Cargo.toml | 2 +- examples/autonatv2/Cargo.toml | 2 +- protocols/autonat/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc710b88a4c..3e9143496e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2654,7 +2654,7 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.41.2" +version = "0.42.0" dependencies = [ "async-std", "either", diff --git a/Cargo.toml b/Cargo.toml index 277214def1e..2cd545f5473 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ libp2p = { version = "0.53.2", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } -libp2p-core = { version = "0.41.2", path = "core" } +libp2p-core = { version = "0.42.0", path = "core" } libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } libp2p-dns = { version = "0.41.1", path = "transports/dns" } libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } diff --git a/core/Cargo.toml b/core/Cargo.toml index 619cd357744..9a3db3de743 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-core" edition = "2021" rust-version = { workspace = true } description = "Core traits and structs of libp2p" -version = "0.41.2" +version = "0.42.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index 7fd9b41f05c..6c862ee22e4 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -3,7 +3,7 @@ name = "autonatv2" version = "0.1.0" edition = "2021" publish = false -license = "MIT" +license = "MIT or Apache-2.0" [package.metadata.release] release = false diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index b20fb4c596c..a981eaf35af 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" version = "0.13.0" -authors = ["David Craven ", "Elena Frank "] +authors = ["David Craven ", "Elena Frank ", "Hannes Furmans "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] From 8095e84d32942854b3eed132cdd92687d6bbb359 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:58:17 +0100 Subject: [PATCH 95/97] Upstream transport changes --- Cargo.lock | 184 +----- Cargo.toml | 3 +- examples/autonat/src/bin/autonat_client.rs | 2 +- examples/autonatv2/Cargo.toml | 38 -- examples/autonatv2/Dockerfile | 20 - examples/autonatv2/docker-compose.yml | 16 - .../autonatv2/src/bin/autonatv2_client.rs | 111 ---- .../autonatv2/src/bin/autonatv2_server.rs | 87 --- examples/autonatv2/src/lib.rs | 1 - misc/server/src/behaviour.rs | 4 +- protocols/autonat/Cargo.toml | 33 +- protocols/autonat/src/{v1 => }/behaviour.rs | 13 +- .../src/{v1 => }/behaviour/as_client.rs | 0 .../src/{v1 => }/behaviour/as_server.rs | 1 + .../autonat/src/{v1 => }/generated/mod.rs | 0 .../src/{v1 => }/generated/structs.proto | 0 .../autonat/src/{v1 => }/generated/structs.rs | 0 protocols/autonat/src/lib.rs | 46 +- protocols/autonat/src/{v1 => }/protocol.rs | 0 protocols/autonat/src/v1.rs | 42 -- protocols/autonat/src/v2.rs | 17 - protocols/autonat/src/v2/client.rs | 5 - protocols/autonat/src/v2/client/behaviour.rs | 439 -------------- protocols/autonat/src/v2/client/handler.rs | 2 - .../src/v2/client/handler/dial_back.rs | 141 ----- .../src/v2/client/handler/dial_request.rs | 332 ---------- protocols/autonat/src/v2/generated/mod.rs | 2 - .../autonat/src/v2/generated/structs.proto | 54 -- protocols/autonat/src/v2/generated/structs.rs | 403 ------------- protocols/autonat/src/v2/protocol.rs | 362 ----------- protocols/autonat/src/v2/server.rs | 5 - protocols/autonat/src/v2/server/behaviour.rs | 155 ----- protocols/autonat/src/v2/server/handler.rs | 8 - .../src/v2/server/handler/dial_back.rs | 140 ----- .../src/v2/server/handler/dial_request.rs | 332 ---------- protocols/autonat/tests/autonatv2.rs | 568 ------------------ protocols/autonat/tests/test_client.rs | 1 - protocols/autonat/tests/test_server.rs | 1 - 38 files changed, 68 insertions(+), 3500 deletions(-) delete mode 100644 examples/autonatv2/Cargo.toml delete mode 100644 examples/autonatv2/Dockerfile delete mode 100644 examples/autonatv2/docker-compose.yml delete mode 100644 examples/autonatv2/src/bin/autonatv2_client.rs delete mode 100644 examples/autonatv2/src/bin/autonatv2_server.rs delete mode 100644 examples/autonatv2/src/lib.rs rename protocols/autonat/src/{v1 => }/behaviour.rs (99%) rename protocols/autonat/src/{v1 => }/behaviour/as_client.rs (100%) rename protocols/autonat/src/{v1 => }/behaviour/as_server.rs (99%) rename protocols/autonat/src/{v1 => }/generated/mod.rs (100%) rename protocols/autonat/src/{v1 => }/generated/structs.proto (100%) rename protocols/autonat/src/{v1 => }/generated/structs.rs (100%) rename protocols/autonat/src/{v1 => }/protocol.rs (100%) delete mode 100644 protocols/autonat/src/v1.rs delete mode 100644 protocols/autonat/src/v2.rs delete mode 100644 protocols/autonat/src/v2/client.rs delete mode 100644 protocols/autonat/src/v2/client/behaviour.rs delete mode 100644 protocols/autonat/src/v2/client/handler.rs delete mode 100644 protocols/autonat/src/v2/client/handler/dial_back.rs delete mode 100644 protocols/autonat/src/v2/client/handler/dial_request.rs delete mode 100644 protocols/autonat/src/v2/generated/mod.rs delete mode 100644 protocols/autonat/src/v2/generated/structs.proto delete mode 100644 protocols/autonat/src/v2/generated/structs.rs delete mode 100644 protocols/autonat/src/v2/protocol.rs delete mode 100644 protocols/autonat/src/v2/server.rs delete mode 100644 protocols/autonat/src/v2/server/behaviour.rs delete mode 100644 protocols/autonat/src/v2/server/handler.rs delete mode 100644 protocols/autonat/src/v2/server/handler/dial_back.rs delete mode 100644 protocols/autonat/src/v2/server/handler/dial_request.rs delete mode 100644 protocols/autonat/tests/autonatv2.rs diff --git a/Cargo.lock b/Cargo.lock index 3e9143496e7..571bee5096e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -476,23 +476,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "autonatv2" -version = "0.1.0" -dependencies = [ - "cfg-if", - "clap", - "libp2p", - "opentelemetry 0.21.0", - "opentelemetry-jaeger", - "opentelemetry_sdk 0.21.2", - "rand 0.8.5", - "tokio", - "tracing", - "tracing-opentelemetry 0.22.0", - "tracing-subscriber", -] - [[package]] name = "axum" version = "0.6.20" @@ -2345,12 +2328,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - [[package]] name = "interceptor" version = "0.10.0" @@ -2607,19 +2584,15 @@ dependencies = [ [[package]] name = "libp2p-autonat" -version = "0.13.0" +version = "0.12.0" dependencies = [ "async-std", "async-trait", "asynchronous-codec", - "bytes", - "either", "futures", - "futures-bounded", "futures-timer", "instant", "libp2p-core", - "libp2p-identify", "libp2p-identity", "libp2p-request-response", "libp2p-swarm", @@ -2627,12 +2600,8 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", - "rand_core 0.6.4", - "thiserror", - "tokio", "tracing", "tracing-subscriber", - "void", ] [[package]] @@ -3695,13 +3664,13 @@ dependencies = [ "futures", "hyper 0.14.27", "libp2p", - "opentelemetry 0.20.0", + "opentelemetry", "opentelemetry-otlp", "opentelemetry_api", "prometheus-client", "tokio", "tracing", - "tracing-opentelemetry 0.21.0", + "tracing-opentelemetry", "tracing-subscriber", ] @@ -4080,39 +4049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk 0.20.0", -] - -[[package]] -name = "opentelemetry" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" -dependencies = [ - "futures-core", - "futures-sink", - "indexmap 2.2.1", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", -] - -[[package]] -name = "opentelemetry-jaeger" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1" -dependencies = [ - "async-trait", - "futures-core", - "futures-util", - "opentelemetry 0.21.0", - "opentelemetry-semantic-conventions 0.13.0", - "opentelemetry_sdk 0.21.2", - "thrift", - "tokio", + "opentelemetry_sdk", ] [[package]] @@ -4125,9 +4062,9 @@ dependencies = [ "futures-core", "http 0.2.9", "opentelemetry-proto", - "opentelemetry-semantic-conventions 0.12.0", + "opentelemetry-semantic-conventions", "opentelemetry_api", - "opentelemetry_sdk 0.20.0", + "opentelemetry_sdk", "prost", "thiserror", "tokio", @@ -4141,7 +4078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk 0.20.0", + "opentelemetry_sdk", "prost", "tonic", ] @@ -4152,16 +4089,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" dependencies = [ - "opentelemetry 0.20.0", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" -dependencies = [ - "opentelemetry 0.21.0", + "opentelemetry", ] [[package]] @@ -4193,7 +4121,7 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", - "ordered-float 3.9.2", + "ordered-float", "percent-encoding", "rand 0.8.5", "regex", @@ -4203,37 +4131,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "opentelemetry_sdk" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "once_cell", - "opentelemetry 0.21.0", - "ordered-float 4.2.0", - "percent-encoding", - "rand 0.8.5", - "thiserror", - "tokio", - "tokio-stream", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-float" version = "3.9.2" @@ -4243,15 +4140,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-float" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" -dependencies = [ - "num-traits", -] - [[package]] name = "overload" version = "0.1.1" @@ -5883,28 +5771,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "thrift" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" -dependencies = [ - "byteorder", - "integer-encoding", - "log", - "ordered-float 2.10.1", - "threadpool", -] - [[package]] name = "time" version = "0.3.23" @@ -6190,8 +6056,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" dependencies = [ "once_cell", - "opentelemetry 0.20.0", - "opentelemetry_sdk 0.20.0", + "opentelemetry", + "opentelemetry_sdk", "smallvec", "tracing", "tracing-core", @@ -6199,24 +6065,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" -dependencies = [ - "js-sys", - "once_cell", - "opentelemetry 0.21.0", - "opentelemetry_sdk 0.21.2", - "smallvec", - "tracing", - "tracing-core", - "tracing-log 0.2.0", - "tracing-subscriber", - "web-time", -] - [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -6608,16 +6456,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webpki-roots" version = "0.25.2" diff --git a/Cargo.toml b/Cargo.toml index 2cd545f5473..55cdf4cf4f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "core", "examples/autonat", - "examples/autonatv2", "examples/browser-webrtc", "examples/chat", "examples/dcutr", @@ -76,7 +75,7 @@ asynchronous-codec = { version = "0.7.0" } futures-bounded = { version = "0.2.3" } libp2p = { version = "0.53.2", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } -libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } +libp2p-autonat = { version = "0.12.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } libp2p-core = { version = "0.42.0", path = "core" } libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 6ef8b90f448..2e85053749f 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -87,7 +87,7 @@ async fn main() -> Result<(), Box> { #[derive(NetworkBehaviour)] struct Behaviour { identify: identify::Behaviour, - auto_nat: autonat::v1::Behaviour, + auto_nat: autonat::Behaviour, } impl Behaviour { diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml deleted file mode 100644 index 6c862ee22e4..00000000000 --- a/examples/autonatv2/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "autonatv2" -version = "0.1.0" -edition = "2021" -publish = false -license = "MIT or Apache-2.0" - -[package.metadata.release] -release = false - -[[bin]] -name = "autonatv2_client" - -[[bin]] -name = "autonatv2_server" - -[dependencies] -libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", "identify", "dns", "quic"] } -clap = { version = "4.4.18", features = ["derive"] } -tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } -tracing = "0.1.40" -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -rand = "0.8.5" -opentelemetry = { version = "0.21.0", optional = true } -opentelemetry_sdk = { version = "0.21.1", optional = true, features = ["rt-tokio"] } -tracing-opentelemetry = { version = "0.22.0", optional = true } -opentelemetry-jaeger = { version = "0.20.0", optional = true, features = ["rt-tokio"] } -cfg-if = "1.0.0" - -[features] -jaeger = ["opentelemetry", "opentelemetry_sdk", "tracing-opentelemetry", "opentelemetry-jaeger"] -opentelemetry = ["dep:opentelemetry"] -opentelemetry_sdk = ["dep:opentelemetry_sdk"] -tracing-opentelemetry = ["dep:tracing-opentelemetry"] -opentelemetry-jaeger = ["dep:opentelemetry-jaeger"] - -[lints] -workspace = true diff --git a/examples/autonatv2/Dockerfile b/examples/autonatv2/Dockerfile deleted file mode 100644 index 5a523649d80..00000000000 --- a/examples/autonatv2/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM rust:1.75-alpine as builder - -RUN apk add musl-dev - -WORKDIR /workspace -COPY . . -RUN --mount=type=cache,target=./target \ - --mount=type=cache,target=/usr/local/cargo/registry \ - cargo build --release --package autonatv2 --bin autonatv2_server -F jaeger - -RUN --mount=type=cache,target=./target \ - mv ./target/release/autonatv2_server /usr/local/bin/autonatv2_server - -FROM alpine:latest - -COPY --from=builder /usr/local/bin/autonatv2_server /app/autonatv2_server - -EXPOSE 4884 - -ENTRYPOINT [ "/app/autonatv2_server", "-l", "4884" ] diff --git a/examples/autonatv2/docker-compose.yml b/examples/autonatv2/docker-compose.yml deleted file mode 100644 index 75f44e7e6f9..00000000000 --- a/examples/autonatv2/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3' - -services: - autonatv2: - build: - context: ../.. - dockerfile: examples/autonatv2/Dockerfile - ports: - - 4884:4884 - jaeger: - image: jaegertracing/all-in-one - ports: - - 6831:6831/udp - - 6832:6832/udp - - 16686:16686 - - 14268:14268 diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs deleted file mode 100644 index de902514dd8..00000000000 --- a/examples/autonatv2/src/bin/autonatv2_client.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::{error::Error, net::Ipv4Addr, time::Duration}; - -use clap::Parser; -use libp2p::{ - autonat, - futures::StreamExt, - identify, identity, - multiaddr::Protocol, - noise, - swarm::{dial_opts::DialOpts, NetworkBehaviour, SwarmEvent}, - tcp, yamux, Multiaddr, SwarmBuilder, -}; -use rand::rngs::OsRng; -use tracing_subscriber::EnvFilter; - -#[derive(Debug, Parser)] -#[clap(name = "libp2p autonatv2 client")] -struct Opt { - /// Port where the client will listen for incoming connections. - #[clap(short = 'p', long, default_value_t = 0)] - listen_port: u16, - - /// Address of the server where want to connect to. - #[clap(short = 'a', long)] - server_address: Multiaddr, - - /// Probe interval in seconds. - #[clap(short = 't', long, default_value = "2")] - probe_interval: u64, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - - let opt = Opt::parse(); - - let mut swarm = SwarmBuilder::with_new_identity() - .with_tokio() - .with_tcp( - tcp::Config::default(), - noise::Config::new, - yamux::Config::default, - )? - .with_quic() - .with_dns()? - .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? - .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) - .build(); - - swarm.listen_on( - Multiaddr::empty() - .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) - .with(Protocol::Tcp(opt.listen_port)), - )?; - - swarm.dial( - DialOpts::unknown_peer_id() - .address(opt.server_address) - .build(), - )?; - - loop { - match swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } => { - println!("Listening on {address:?}"); - } - SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { - server, - tested_addr, - bytes_sent, - result: Ok(()), - })) => { - println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Everything Ok and verified."); - } - SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { - server, - tested_addr, - bytes_sent, - result: Err(e), - })) => { - println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Failed with {e:?}."); - } - SwarmEvent::ExternalAddrConfirmed { address } => { - println!("External address confirmed: {address}"); - } - _ => {} - } - } -} - -#[derive(NetworkBehaviour)] -pub struct Behaviour { - autonat: autonat::v2::client::Behaviour, - identify: identify::Behaviour, -} - -impl Behaviour { - pub fn new(key: identity::PublicKey, probe_interval: u64) -> Self { - Self { - autonat: autonat::v2::client::Behaviour::new( - OsRng, - autonat::v2::client::Config::default() - .with_probe_interval(Duration::from_secs(probe_interval)), - ), - identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), - } - } -} diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs deleted file mode 100644 index 849ed3b3b0a..00000000000 --- a/examples/autonatv2/src/bin/autonatv2_server.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::{error::Error, net::Ipv4Addr, time::Duration}; - -use cfg_if::cfg_if; -use clap::Parser; -use libp2p::{ - autonat, - futures::StreamExt, - identify, identity, - multiaddr::Protocol, - noise, - swarm::{NetworkBehaviour, SwarmEvent}, - tcp, yamux, Multiaddr, SwarmBuilder, -}; -use rand::rngs::OsRng; - -#[derive(Debug, Parser)] -#[clap(name = "libp2p autonatv2 server")] -struct Opt { - #[clap(short, long, default_value_t = 0)] - listen_port: u16, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - cfg_if! { - if #[cfg(feature = "jaeger")] { - use tracing_subscriber::layer::SubscriberExt; - use opentelemetry_sdk::runtime::Tokio; - let tracer = opentelemetry_jaeger::new_agent_pipeline() - .with_endpoint("jaeger:6831") - .with_service_name("autonatv2") - .install_batch(Tokio)?; - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - let subscriber = tracing_subscriber::Registry::default() - .with(telemetry); - } else { - let subscriber = tracing_subscriber::fmt() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .finish(); - } - } - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); - - let opt = Opt::parse(); - - let mut swarm = SwarmBuilder::with_new_identity() - .with_tokio() - .with_tcp( - tcp::Config::default(), - noise::Config::new, - yamux::Config::default, - )? - .with_quic() - .with_dns()? - .with_behaviour(|key| Behaviour::new(key.public()))? - .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) - .build(); - - swarm.listen_on( - Multiaddr::empty() - .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) - .with(Protocol::Tcp(opt.listen_port)), - )?; - - loop { - match swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), - SwarmEvent::Behaviour(event) => println!("{event:?}"), - e => println!("{e:?}"), - } - } -} - -#[derive(NetworkBehaviour)] -pub struct Behaviour { - autonat: autonat::v2::server::Behaviour, - identify: identify::Behaviour, -} - -impl Behaviour { - pub fn new(key: identity::PublicKey) -> Self { - Self { - autonat: autonat::v2::server::Behaviour::new(OsRng), - identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), - } - } -} diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs deleted file mode 100644 index 8b137891791..00000000000 --- a/examples/autonatv2/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index a6d08689dfb..c5bcb7e9d9a 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -26,7 +26,7 @@ pub(crate) struct Behaviour { ping: ping::Behaviour, identify: identify::Behaviour, pub(crate) kademlia: Toggle>, - autonat: Toggle, + autonat: Toggle, } impl Behaviour { @@ -59,7 +59,7 @@ impl Behaviour { .into(); let autonat = if enable_autonat { - Some(autonat::v1::Behaviour::new( + Some(autonat::Behaviour::new( PeerId::from(pub_key.clone()), Default::default(), )) diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index a981eaf35af..fce64ad0c12 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -3,50 +3,32 @@ name = "libp2p-autonat" edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" -version = "0.13.0" -authors = ["David Craven ", "Elena Frank ", "Hannes Furmans "] +authors = ["David Craven ", "Elena Frank "] +version = "0.12.0" license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] - [dependencies] async-trait = "0.1" -asynchronous-codec = { workspace = true } -bytes = { version = "1", optional = true } -either = { version = "1.9.0", optional = true } futures = "0.3" -futures-bounded = { workspace = true, optional = true } futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } -libp2p-identity = { workspace = true } -libp2p-request-response = { workspace = true, optional = true } libp2p-swarm = { workspace = true } +libp2p-request-response = { workspace = true } +libp2p-identity = { workspace = true } quick-protobuf = "0.8" -quick-protobuf-codec = { workspace = true } rand = "0.8" -rand_core = { version = "0.6", optional = true } -thiserror = { version = "1.0.52", optional = true } tracing = "0.1.37" -void = { version = "1", optional = true } +quick-protobuf-codec = { workspace = true } +asynchronous-codec = { workspace = true } [dev-dependencies] -tokio = { version = "1", features = ["macros", "rt", "sync"]} async-std = { version = "1.10", features = ["attributes"] } libp2p-swarm-test = { path = "../../swarm-test" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } -libp2p-identify = { workspace = true } -libp2p-swarm = { workspace = true, features = ["macros"]} - -[features] -default = ["v1", "v2"] -v1 = ["dep:libp2p-request-response"] -v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] - -[lints] -workspace = true # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling @@ -54,3 +36,6 @@ workspace = true all-features = true rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/protocols/autonat/src/v1/behaviour.rs b/protocols/autonat/src/behaviour.rs similarity index 99% rename from protocols/autonat/src/v1/behaviour.rs rename to protocols/autonat/src/behaviour.rs index 6e2c36a467f..bf36080a721 100644 --- a/protocols/autonat/src/v1/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -339,7 +339,7 @@ impl Behaviour { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, - port_use: _, + .. } => { if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { self.pending_actions @@ -349,7 +349,7 @@ impl Behaviour { ConnectedPoint::Dialer { address: _, role_override: Endpoint::Listener, - port_use: _, + .. } => { // Outgoing connection was dialed as a listener. In other words outgoing connection // was dialed as part of a hole punch. `libp2p-autonat` never attempts to hole @@ -517,13 +517,8 @@ impl NetworkBehaviour for Behaviour { role_override: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner.handle_established_outbound_connection( - connection_id, - peer, - addr, - role_override, - port_use, - ) + self.inner + .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/protocols/autonat/src/v1/behaviour/as_client.rs b/protocols/autonat/src/behaviour/as_client.rs similarity index 100% rename from protocols/autonat/src/v1/behaviour/as_client.rs rename to protocols/autonat/src/behaviour/as_client.rs diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/behaviour/as_server.rs similarity index 99% rename from protocols/autonat/src/v1/behaviour/as_server.rs rename to protocols/autonat/src/behaviour/as_server.rs index e309023bc75..4e3cfc77891 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/behaviour/as_server.rs @@ -17,6 +17,7 @@ // 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 super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, ResponseError, diff --git a/protocols/autonat/src/v1/generated/mod.rs b/protocols/autonat/src/generated/mod.rs similarity index 100% rename from protocols/autonat/src/v1/generated/mod.rs rename to protocols/autonat/src/generated/mod.rs diff --git a/protocols/autonat/src/v1/generated/structs.proto b/protocols/autonat/src/generated/structs.proto similarity index 100% rename from protocols/autonat/src/v1/generated/structs.proto rename to protocols/autonat/src/generated/structs.proto diff --git a/protocols/autonat/src/v1/generated/structs.rs b/protocols/autonat/src/generated/structs.rs similarity index 100% rename from protocols/autonat/src/v1/generated/structs.rs rename to protocols/autonat/src/generated/structs.rs diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index a6fc66b28d1..10c87b1e984 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1,9 +1,41 @@ -#[cfg(feature = "v1")] -pub mod v1; +// Copyright 2021 Protocol Labs. +// +// 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. -#[cfg(feature = "v2")] -pub mod v2; +//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. -#[cfg(feature = "v1")] -#[allow(deprecated)] -pub use v1::*; +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod behaviour; +mod protocol; + +pub use self::{ + behaviour::{ + Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, + OutboundProbeError, OutboundProbeEvent, ProbeId, + }, + protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, +}; +pub use libp2p_request_response::{InboundFailure, OutboundFailure}; + +mod proto { + #![allow(unreachable_pub)] + include!("generated/mod.rs"); + pub(crate) use self::structs::{mod_Message::*, Message}; +} diff --git a/protocols/autonat/src/v1/protocol.rs b/protocols/autonat/src/protocol.rs similarity index 100% rename from protocols/autonat/src/v1/protocol.rs rename to protocols/autonat/src/protocol.rs diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs deleted file mode 100644 index 07b08310871..00000000000 --- a/protocols/autonat/src/v1.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 Protocol Labs. -// -// 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 [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. - -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![cfg_attr(not(test), deprecated(note = "Please use `v2` module instead."))] - -pub(crate) mod behaviour; -pub(crate) mod protocol; - -pub use self::{ - behaviour::{ - Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, - OutboundProbeError, OutboundProbeEvent, ProbeId, - }, - protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, -}; -pub use libp2p_request_response::{InboundFailure, OutboundFailure}; - -pub(crate) mod proto { - #![allow(unreachable_pub)] - include!("v1/generated/mod.rs"); - pub(crate) use self::structs::{mod_Message::*, Message}; -} diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs deleted file mode 100644 index 8cab10a1d14..00000000000 --- a/protocols/autonat/src/v2.rs +++ /dev/null @@ -1,17 +0,0 @@ -use libp2p_swarm::StreamProtocol; - -pub mod client; -pub(crate) mod protocol; -pub mod server; - -pub(crate) mod generated { - #![allow(unreachable_pub)] - include!("v2/generated/mod.rs"); -} - -pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = - StreamProtocol::new("/libp2p/autonat/2/dial-request"); -pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = - StreamProtocol::new("/libp2p/autonat/2/dial-back"); - -type Nonce = u64; diff --git a/protocols/autonat/src/v2/client.rs b/protocols/autonat/src/v2/client.rs deleted file mode 100644 index d3272512f35..00000000000 --- a/protocols/autonat/src/v2/client.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod behaviour; -mod handler; - -pub use behaviour::Event; -pub use behaviour::{Behaviour, Config}; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs deleted file mode 100644 index 97509c05443..00000000000 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ /dev/null @@ -1,439 +0,0 @@ -use std::{ - collections::{HashMap, VecDeque}, - task::{Context, Poll}, - time::Duration, -}; - -use either::Either; -use futures::FutureExt; -use futures_timer::Delay; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, - ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, -}; -use rand::prelude::*; -use rand_core::OsRng; -use std::fmt::{Debug, Display, Formatter}; - -use crate::v2::{protocol::DialRequest, Nonce}; - -use super::handler::{ - dial_back::{self, IncomingNonce}, - dial_request, -}; - -#[derive(Debug, Clone, Copy)] -pub struct Config { - /// How many candidates we will test at most. - pub(crate) max_candidates: usize, - - /// The interval at which we will attempt to confirm candidates as external addresses. - pub(crate) probe_interval: Duration, -} - -impl Config { - pub fn with_max_candidates(self, max_candidates: usize) -> Self { - Self { - max_candidates, - ..self - } - } - - pub fn with_probe_interval(self, probe_interval: Duration) -> Self { - Self { - probe_interval, - ..self - } - } -} - -impl Default for Config { - fn default() -> Self { - Self { - max_candidates: 10, - probe_interval: Duration::from_secs(5), - } - } -} - -pub struct Behaviour -where - R: RngCore + 'static, -{ - rng: R, - config: Config, - pending_events: VecDeque< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - >, - address_candidates: HashMap, - next_tick: Delay, - peer_info: HashMap, -} - -impl NetworkBehaviour for Behaviour -where - R: RngCore + 'static, -{ - type ConnectionHandler = Either; - - type ToSwarm = Event; - - fn handle_established_inbound_connection( - &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: &Multiaddr, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Right(dial_back::Handler::new())) - } - - fn handle_established_outbound_connection( - &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: Endpoint, - _: PortUse, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Left(dial_request::Handler::new())) - } - - fn on_swarm_event(&mut self, event: FromSwarm) { - match event { - FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - self.address_candidates - .entry(addr.clone()) - .or_default() - .score += 1; - } - FromSwarm::ConnectionEstablished(ConnectionEstablished { - peer_id, - connection_id, - endpoint: _, - .. - }) => { - self.peer_info.insert( - connection_id, - ConnectionInfo { - peer_id, - supports_autonat: false, - }, - ); - } - FromSwarm::ConnectionClosed(ConnectionClosed { - peer_id, - connection_id, - .. - }) => { - let info = self - .peer_info - .remove(&connection_id) - .expect("inconsistent state"); - - if info.supports_autonat { - tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); - } - } - _ => {} - } - } - - fn on_connection_handler_event( - &mut self, - peer_id: PeerId, - connection_id: ConnectionId, - event: ::ToBehaviour, - ) { - let (nonce, outcome) = match event { - Either::Right(IncomingNonce { nonce, sender }) => { - let Some((_, info)) = self - .address_candidates - .iter_mut() - .find(|(_, info)| info.is_pending_with_nonce(nonce)) - else { - let _ = sender.send(Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!("Received unexpected nonce: {nonce} from {peer_id}"), - ))); - return; - }; - - info.status = TestStatus::Received(nonce); - tracing::debug!(%peer_id, %nonce, "Successful dial-back"); - - let _ = sender.send(Ok(())); - - return; - } - Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = true; - return; - } - Either::Left(dial_request::ToBehaviour::TestOutcome { nonce, outcome }) => { - (nonce, outcome) - } - }; - - let ((tested_addr, bytes_sent), result) = match outcome { - Ok(address) => { - let received_dial_back = self - .address_candidates - .iter_mut() - .any(|(_, info)| info.is_received_with_nonce(nonce)); - - if !received_dial_back { - tracing::warn!( - %peer_id, - %nonce, - "Server reported reachbility but we never received a dial-back" - ); - return; - } - - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(address.0.clone())); - - (address, Ok(())) - } - Err(dial_request::Error::UnsupportedProtocol) => { - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = false; - - self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. - - return; - } - Err(dial_request::Error::Io(e)) => { - tracing::debug!( - %peer_id, - %nonce, - "Failed to complete AutoNAT probe: {e}" - ); - - self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. - - return; - } - Err(dial_request::Error::AddressNotReachable { - address, - bytes_sent, - error, - }) => { - self.reset_status_to(nonce, TestStatus::Failed); - - ((address, bytes_sent), Err(error)) - } - }; - - self.pending_events.push_back(ToSwarm::GenerateEvent(Event { - tested_addr, - bytes_sent, - server: peer_id, - result: result.map_err(|e| Error { inner: e }), - })); - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll::FromBehaviour>> - { - loop { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - - if self.next_tick.poll_unpin(cx).is_ready() { - self.next_tick.reset(self.config.probe_interval); - - self.issue_dial_requests_for_untested_candidates(); - continue; - } - - return Poll::Pending; - } - } -} - -impl Behaviour -where - R: RngCore + 'static, -{ - pub fn new(rng: R, config: Config) -> Self { - Self { - rng, - next_tick: Delay::new(config.probe_interval), - config, - pending_events: VecDeque::new(), - address_candidates: HashMap::new(), - peer_info: HashMap::new(), - } - } - - /// Issues dial requests to random AutoNAT servers for the most frequently reported, untested candidates. - /// - /// In the current implementation, we only send a single address to each AutoNAT server. - /// This spreads our candidates out across all servers we are connected to which should give us pretty fast feedback on all of them. - fn issue_dial_requests_for_untested_candidates(&mut self) { - for addr in self.untested_candidates() { - let Some((conn_id, peer_id)) = self.random_autonat_server() else { - tracing::debug!("Not connected to any AutoNAT servers"); - return; - }; - - let nonce = self.rng.gen(); - self.address_candidates - .get_mut(&addr) - .expect("only emit candidates") - .status = TestStatus::Pending(nonce); - - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(conn_id), - event: Either::Left(DialRequest { - nonce, - addrs: vec![addr], - }), - }); - } - } - - /// Returns all untested candidates, sorted by the frequency they were reported at. - /// - /// More frequently reported candidates are considered to more likely be external addresses and thus tested first. - fn untested_candidates(&self) -> impl Iterator { - let mut entries = self - .address_candidates - .iter() - .filter(|(_, info)| info.status == TestStatus::Untested) - .map(|(addr, count)| (addr.clone(), *count)) - .collect::>(); - - entries.sort_unstable_by_key(|(_, info)| info.score); - - if entries.is_empty() { - tracing::debug!("No untested address candidates"); - } - - entries - .into_iter() - .rev() // `sort_unstable` is ascending - .take(self.config.max_candidates) - .map(|(addr, _)| addr) - } - - /// Chooses an active connection to one of our peers that reported support for the [`DIAL_REQUEST_PROTOCOL`](crate::v2::DIAL_REQUEST_PROTOCOL) protocol. - fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { - let (conn_id, info) = self - .peer_info - .iter() - .filter(|(_, info)| info.supports_autonat) - .choose(&mut self.rng)?; - - Some((*conn_id, info.peer_id)) - } - - fn reset_status_to(&mut self, nonce: Nonce, new_status: TestStatus) { - let Some((_, info)) = self - .address_candidates - .iter_mut() - .find(|(_, i)| i.is_pending_with_nonce(nonce) || i.is_received_with_nonce(nonce)) - else { - return; - }; - - info.status = new_status; - } - - // FIXME: We don't want test-only APIs in our public API. - #[doc(hidden)] - pub fn validate_addr(&mut self, addr: &Multiaddr) { - if let Some(info) = self.address_candidates.get_mut(addr) { - info.status = TestStatus::Received(self.rng.next_u64()); - } - } -} - -impl Default for Behaviour { - fn default() -> Self { - Self::new(OsRng, Config::default()) - } -} - -pub struct Error { - pub(crate) inner: dial_request::DialBackError, -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Debug for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self.inner, f) - } -} - -#[derive(Debug)] -pub struct Event { - /// The address that was selected for testing. - pub tested_addr: Multiaddr, - /// The amount of data that was sent to the server. - /// Is 0 if it wasn't necessary to send any data. - /// Otherwise it's a number between 30.000 and 100.000. - pub bytes_sent: usize, - /// The peer id of the server that was selected for testing. - pub server: PeerId, - /// The result of the test. If the test was successful, this is `Ok(())`. - /// Otherwise it's an error. - pub result: Result<(), Error>, -} - -struct ConnectionInfo { - peer_id: PeerId, - supports_autonat: bool, -} - -#[derive(Copy, Clone, Default)] -struct AddressInfo { - score: usize, - status: TestStatus, -} - -impl AddressInfo { - fn is_pending_with_nonce(&self, nonce: Nonce) -> bool { - match self.status { - TestStatus::Pending(c) => c == nonce, - _ => false, - } - } - - fn is_received_with_nonce(&self, nonce: Nonce) -> bool { - match self.status { - TestStatus::Received(c) => c == nonce, - _ => false, - } - } -} - -#[derive(Clone, Copy, Default, PartialEq)] -enum TestStatus { - #[default] - Untested, - Pending(Nonce), - Failed, - Received(Nonce), -} diff --git a/protocols/autonat/src/v2/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs deleted file mode 100644 index e526c2fb44c..00000000000 --- a/protocols/autonat/src/v2/client/handler.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod dial_back; -pub(crate) mod dial_request; diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs deleted file mode 100644 index b94580e69ba..00000000000 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::{ - io, - task::{Context, Poll}, - time::Duration, -}; - -use futures::channel::oneshot; -use futures_bounded::StreamSet; -use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; -use libp2p_swarm::{ - handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, -}; -use void::Void; - -use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; - -pub struct Handler { - inbound: StreamSet>, -} - -impl Handler { - pub(crate) fn new() -> Self { - Self { - inbound: StreamSet::new(Duration::from_secs(5), 2), - } - } -} - -impl ConnectionHandler for Handler { - type FromBehaviour = Void; - type ToBehaviour = IncomingNonce; - type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = DeniedUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - loop { - match self.inbound.poll_next_unpin(cx) { - Poll::Pending => return Poll::Pending, - Poll::Ready(None) => continue, - Poll::Ready(Some(Err(err))) => { - tracing::debug!("Stream timed out: {err}"); - continue; - } - Poll::Ready(Some(Ok(Err(err)))) => { - tracing::debug!("Dial back handler failed with: {err:?}"); - continue; - } - Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)); - } - } - } - } - - fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - if self.inbound.try_push(perform_dial_back(protocol)).is_err() { - tracing::warn!("Dial back request dropped, too many requests in flight"); - } - } - ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { - void::unreachable(error); - } - _ => {} - } - } -} - -struct State { - stream: libp2p_swarm::Stream, - oneshot: Option>>, -} - -#[derive(Debug)] -pub struct IncomingNonce { - pub nonce: Nonce, - pub sender: oneshot::Sender>, -} - -fn perform_dial_back( - stream: libp2p_swarm::Stream, -) -> impl futures::Stream> { - let state = State { - stream, - oneshot: None, - }; - futures::stream::unfold(state, |mut state| async move { - if let Some(ref mut receiver) = state.oneshot { - match receiver.await { - Ok(Ok(())) => {} - Ok(Err(e)) => return Some((Err(e), state)), - Err(_) => { - return Some(( - Err(io::Error::new(io::ErrorKind::Other, "Sender got cancelled")), - state, - )); - } - } - if let Err(e) = protocol::dial_back_response(&mut state.stream).await { - return Some((Err(e), state)); - } - return None; - } - - let nonce = match protocol::recv_dial_back(&mut state.stream).await { - Ok(nonce) => nonce, - Err(err) => { - return Some((Err(err), state)); - } - }; - - let (sender, receiver) = oneshot::channel(); - state.oneshot = Some(receiver); - Some((Ok(IncomingNonce { nonce, sender }), state)) - }) -} diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs deleted file mode 100644 index 5191382734f..00000000000 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ /dev/null @@ -1,332 +0,0 @@ -use futures::{channel::oneshot, AsyncWrite}; -use futures_bounded::FuturesMap; -use libp2p_core::{ - upgrade::{DeniedUpgrade, ReadyUpgrade}, - Multiaddr, -}; - -use libp2p_swarm::{ - handler::{ - ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, - ProtocolsChange, - }, - ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, - SubstreamProtocol, -}; -use std::{ - collections::VecDeque, - io, - iter::{once, repeat}, - task::{Context, Poll}, - time::Duration, -}; - -use crate::v2::{ - generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - protocol::{ - Coder, DialDataRequest, DialDataResponse, DialRequest, Response, - DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, - }, - Nonce, DIAL_REQUEST_PROTOCOL, -}; - -#[derive(Debug)] -pub enum ToBehaviour { - TestOutcome { - nonce: Nonce, - outcome: Result<(Multiaddr, usize), Error>, - }, - PeerHasServerSupport, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Address is not reachable: {error}")] - AddressNotReachable { - address: Multiaddr, - bytes_sent: usize, - error: DialBackError, - }, - #[error("Peer does not support AutoNAT dial-request protocol")] - UnsupportedProtocol, - #[error("IO error: {0}")] - Io(io::Error), -} - -impl From for Error { - fn from(value: io::Error) -> Self { - Self::Io(value) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum DialBackError { - #[error("server failed to establish a connection")] - NoConnection, - #[error("dial back stream failed")] - StreamFailed, -} - -pub struct Handler { - queued_events: VecDeque< - ConnectionHandlerEvent< - ::OutboundProtocol, - ::OutboundOpenInfo, - ::ToBehaviour, - >, - >, - outbound: FuturesMap>, - queued_streams: VecDeque< - oneshot::Sender< - Result< - Stream, - StreamUpgradeError< as OutboundUpgradeSend>::Error>, - >, - >, - >, -} - -impl Handler { - pub(crate) fn new() -> Self { - Self { - queued_events: VecDeque::new(), - outbound: FuturesMap::new(Duration::from_secs(10), 10), - queued_streams: VecDeque::default(), - } - } - - fn perform_request(&mut self, req: DialRequest) { - let (tx, rx) = oneshot::channel(); - self.queued_streams.push_back(tx); - self.queued_events - .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()), - }); - if self - .outbound - .try_push(req.nonce, start_stream_handle(req, rx)) - .is_err() - { - tracing::debug!("Dial request dropped, too many requests in flight"); - } - } -} - -impl ConnectionHandler for Handler { - type FromBehaviour = DialRequest; - type ToBehaviour = ToBehaviour; - type InboundProtocol = DeniedUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(DeniedUpgrade, ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - if let Some(event) = self.queued_events.pop_front() { - return Poll::Ready(event); - } - - match self.outbound.poll_unpin(cx) { - Poll::Ready((nonce, Ok(outcome))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestOutcome { nonce, outcome }, - )) - } - Poll::Ready((nonce, Err(_))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestOutcome { - nonce, - outcome: Err(Error::Io(io::ErrorKind::TimedOut.into())), - }, - )); - } - Poll::Pending => {} - } - - Poll::Pending - } - - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - self.perform_request(event); - } - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { - tracing::debug!("Dial request failed: {}", error); - match self.queued_streams.pop_front() { - Some(stream_tx) => { - let _ = stream_tx.send(Err(error)); - } - None => { - tracing::warn!( - "Opened unexpected substream without a pending dial request" - ); - } - } - } - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, .. - }) => match self.queued_streams.pop_front() { - Some(stream_tx) => { - if stream_tx.send(Ok(protocol)).is_err() { - tracing::debug!("Failed to send stream to dead handler"); - } - } - None => { - tracing::warn!("Opened unexpected substream without a pending dial request"); - } - }, - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { - if added.any(|p| p.as_ref() == DIAL_REQUEST_PROTOCOL) { - self.queued_events - .push_back(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::PeerHasServerSupport, - )); - } - } - _ => {} - } - } -} - -async fn start_stream_handle( - req: DialRequest, - stream_recv: oneshot::Receiver>>, -) -> Result<(Multiaddr, usize), Error> { - let stream = stream_recv - .await - .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))? - .map_err(|e| match e { - StreamUpgradeError::NegotiationFailed => Error::UnsupportedProtocol, - StreamUpgradeError::Timeout => Error::Io(io::ErrorKind::TimedOut.into()), - StreamUpgradeError::Apply(v) => void::unreachable(v), - StreamUpgradeError::Io(e) => Error::Io(e), - })?; - - let mut coder = Coder::new(stream); - coder.send(req.clone()).await?; - - let (res, bytes_sent) = match coder.next().await? { - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => { - if addr_idx >= req.addrs.len() { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "address index out of bounds", - ))); - } - if !(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND).contains(&num_bytes) { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "requested bytes out of bounds", - ))); - } - - send_aap_data(&mut coder, num_bytes).await?; - - let Response::Dial(dial_response) = coder.next().await? else { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "expected message", - ))); - }; - - (dial_response, num_bytes) - } - Response::Dial(dial_response) => (dial_response, 0), - }; - coder.close().await?; - - match res.status { - ResponseStatus::E_REQUEST_REJECTED => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "server rejected request", - ))) - } - ResponseStatus::E_DIAL_REFUSED => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "server refused dial", - ))) - } - ResponseStatus::E_INTERNAL_ERROR => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "server encountered internal error", - ))) - } - ResponseStatus::OK => {} - } - - let tested_address = req - .addrs - .get(res.addr_idx) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "address index out of bounds"))? - .clone(); - - match res.dial_status { - DialStatus::UNUSED => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "unexpected message", - ))) - } - DialStatus::E_DIAL_ERROR => { - return Err(Error::AddressNotReachable { - address: tested_address, - bytes_sent, - error: DialBackError::NoConnection, - }) - } - DialStatus::E_DIAL_BACK_ERROR => { - return Err(Error::AddressNotReachable { - address: tested_address, - bytes_sent, - error: DialBackError::StreamFailed, - }) - } - DialStatus::OK => {} - } - - Ok((tested_address, bytes_sent)) -} - -async fn send_aap_data(stream: &mut Coder, num_bytes: usize) -> io::Result<()> -where - I: AsyncWrite + Unpin, -{ - let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; - let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; - for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) - .take(count_full) - .chain(once(partial_len)) - .filter(|e| *e > 0) - .map(|data_count| { - DialDataResponse::new(data_count).expect("data count is unexpectedly too big") - }) - { - stream.send(req).await?; - } - - Ok(()) -} diff --git a/protocols/autonat/src/v2/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs deleted file mode 100644 index e52c5a80bc0..00000000000 --- a/protocols/autonat/src/v2/generated/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Automatically generated mod.rs -pub mod structs; diff --git a/protocols/autonat/src/v2/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto deleted file mode 100644 index 31791463956..00000000000 --- a/protocols/autonat/src/v2/generated/structs.proto +++ /dev/null @@ -1,54 +0,0 @@ -syntax = "proto2"; - -package structs; - -message Message { - oneof msg { - DialRequest dialRequest = 1; - DialResponse dialResponse = 2; - DialDataRequest dialDataRequest = 3; - DialDataResponse dialDataResponse = 4; - } -} - -message DialRequest { - repeated bytes addrs = 1; - fixed64 nonce = 2; -} - -message DialDataRequest { - uint32 addrIdx = 1; - uint64 numBytes = 2; -} - -enum DialStatus { - UNUSED = 0; - E_DIAL_ERROR = 100; - E_DIAL_BACK_ERROR = 101; - OK = 200; -} - -message DialResponse { - enum ResponseStatus { - E_INTERNAL_ERROR = 0; - E_REQUEST_REJECTED = 100; - E_DIAL_REFUSED = 101; - OK = 200; - } - - ResponseStatus status = 1; - uint32 addrIdx = 2; - DialStatus dialStatus = 3; -} - -message DialDataResponse { bytes data = 1; } - -message DialBack { fixed64 nonce = 1; } - -message DialBackResponse { - enum DialBackStatus { - OK = 0; - } - - DialBackStatus status = 1; -} diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs deleted file mode 100644 index 12568dd0364..00000000000 --- a/protocols/autonat/src/v2/generated/structs.rs +++ /dev/null @@ -1,403 +0,0 @@ -// Automatically generated rust module for 'structs.proto' file - -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(unused_imports)] -#![allow(unknown_lints)] -#![allow(clippy::all)] -#![cfg_attr(rustfmt, rustfmt_skip)] - - -use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; -use quick_protobuf::sizeofs::*; -use super::*; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DialStatus { - UNUSED = 0, - E_DIAL_ERROR = 100, - E_DIAL_BACK_ERROR = 101, - OK = 200, -} - -impl Default for DialStatus { - fn default() -> Self { - DialStatus::UNUSED - } -} - -impl From for DialStatus { - fn from(i: i32) -> Self { - match i { - 0 => DialStatus::UNUSED, - 100 => DialStatus::E_DIAL_ERROR, - 101 => DialStatus::E_DIAL_BACK_ERROR, - 200 => DialStatus::OK, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for DialStatus { - fn from(s: &'a str) -> Self { - match s { - "UNUSED" => DialStatus::UNUSED, - "E_DIAL_ERROR" => DialStatus::E_DIAL_ERROR, - "E_DIAL_BACK_ERROR" => DialStatus::E_DIAL_BACK_ERROR, - "OK" => DialStatus::OK, - _ => Self::default(), - } - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct Message { - pub msg: structs::mod_Message::OneOfmsg, -} - -impl<'a> MessageRead<'a> for Message { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.msg = structs::mod_Message::OneOfmsg::dialRequest(r.read_message::(bytes)?), - Ok(18) => msg.msg = structs::mod_Message::OneOfmsg::dialResponse(r.read_message::(bytes)?), - Ok(26) => msg.msg = structs::mod_Message::OneOfmsg::dialDataRequest(r.read_message::(bytes)?), - Ok(34) => msg.msg = structs::mod_Message::OneOfmsg::dialDataResponse(r.read_message::(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for Message { - fn get_size(&self) -> usize { - 0 - + match self.msg { - structs::mod_Message::OneOfmsg::dialRequest(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::dialResponse(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::None => 0, - } } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - match self.msg { structs::mod_Message::OneOfmsg::dialRequest(ref m) => { w.write_with_tag(10, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::dialResponse(ref m) => { w.write_with_tag(18, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => { w.write_with_tag(26, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => { w.write_with_tag(34, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::None => {}, - } Ok(()) - } -} - -pub mod mod_Message { - -use super::*; - -#[derive(Debug, PartialEq, Clone)] -pub enum OneOfmsg { - dialRequest(structs::DialRequest), - dialResponse(structs::DialResponse), - dialDataRequest(structs::DialDataRequest), - dialDataResponse(structs::DialDataResponse), - None, -} - -impl Default for OneOfmsg { - fn default() -> Self { - OneOfmsg::None - } -} - -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialRequest { - pub addrs: Vec>, - pub nonce: Option, -} - -impl<'a> MessageRead<'a> for DialRequest { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), - Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialRequest { - fn get_size(&self) -> usize { - 0 - + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() - + self.nonce.as_ref().map_or(0, |_| 1 + 8) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - for s in &self.addrs { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } - if let Some(ref s) = self.nonce { w.write_with_tag(17, |w| w.write_fixed64(*s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialDataRequest { - pub addrIdx: Option, - pub numBytes: Option, -} - -impl<'a> MessageRead<'a> for DialDataRequest { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.addrIdx = Some(r.read_uint32(bytes)?), - Ok(16) => msg.numBytes = Some(r.read_uint64(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialDataRequest { - fn get_size(&self) -> usize { - 0 - + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.numBytes.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.addrIdx { w.write_with_tag(8, |w| w.write_uint32(*s))?; } - if let Some(ref s) = self.numBytes { w.write_with_tag(16, |w| w.write_uint64(*s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialResponse { - pub status: Option, - pub addrIdx: Option, - pub dialStatus: Option, -} - -impl<'a> MessageRead<'a> for DialResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), - Ok(16) => msg.addrIdx = Some(r.read_uint32(bytes)?), - Ok(24) => msg.dialStatus = Some(r.read_enum(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialResponse { - fn get_size(&self) -> usize { - 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.dialStatus.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - if let Some(ref s) = self.addrIdx { w.write_with_tag(16, |w| w.write_uint32(*s))?; } - if let Some(ref s) = self.dialStatus { w.write_with_tag(24, |w| w.write_enum(*s as i32))?; } - Ok(()) - } -} - -pub mod mod_DialResponse { - - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum ResponseStatus { - E_INTERNAL_ERROR = 0, - E_REQUEST_REJECTED = 100, - E_DIAL_REFUSED = 101, - OK = 200, -} - -impl Default for ResponseStatus { - fn default() -> Self { - ResponseStatus::E_INTERNAL_ERROR - } -} - -impl From for ResponseStatus { - fn from(i: i32) -> Self { - match i { - 0 => ResponseStatus::E_INTERNAL_ERROR, - 100 => ResponseStatus::E_REQUEST_REJECTED, - 101 => ResponseStatus::E_DIAL_REFUSED, - 200 => ResponseStatus::OK, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for ResponseStatus { - fn from(s: &'a str) -> Self { - match s { - "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, - "E_REQUEST_REJECTED" => ResponseStatus::E_REQUEST_REJECTED, - "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, - "OK" => ResponseStatus::OK, - _ => Self::default(), - } - } -} - -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialDataResponse { - pub data: Option>, -} - -impl<'a> MessageRead<'a> for DialDataResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned()), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialDataResponse { - fn get_size(&self) -> usize { - 0 - + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.data { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialBack { - pub nonce: Option, -} - -impl<'a> MessageRead<'a> for DialBack { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(9) => msg.nonce = Some(r.read_fixed64(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialBack { - fn get_size(&self) -> usize { - 0 - + self.nonce.as_ref().map_or(0, |_| 1 + 8) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.nonce { w.write_with_tag(9, |w| w.write_fixed64(*s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialBackResponse { - pub status: Option, -} - -impl<'a> MessageRead<'a> for DialBackResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialBackResponse { - fn get_size(&self) -> usize { - 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - Ok(()) - } -} - -pub mod mod_DialBackResponse { - - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DialBackStatus { - OK = 0, -} - -impl Default for DialBackStatus { - fn default() -> Self { - DialBackStatus::OK - } -} - -impl From for DialBackStatus { - fn from(i: i32) -> Self { - match i { - 0 => DialBackStatus::OK, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for DialBackStatus { - fn from(s: &'a str) -> Self { - match s { - "OK" => DialBackStatus::OK, - _ => Self::default(), - } - } -} - -} - diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs deleted file mode 100644 index d0e2fc30ca6..00000000000 --- a/protocols/autonat/src/v2/protocol.rs +++ /dev/null @@ -1,362 +0,0 @@ -// change to quick-protobuf-codec - -use std::io; -use std::io::ErrorKind; - -use asynchronous_codec::{Framed, FramedRead, FramedWrite}; - -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; -use libp2p_core::Multiaddr; - -use quick_protobuf_codec::Codec; -use rand::Rng; - -use crate::v2::{generated::structs as proto, Nonce}; - -const REQUEST_MAX_SIZE: usize = 4104; -pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; -pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; -pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; - -fn new_io_invalid_data_err(msg: impl Into) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, msg.into()) -} - -macro_rules! ok_or_invalid_data { - ($field:ident) => { - $field.ok_or_else(|| new_io_invalid_data_err(concat!(stringify!($field), " is missing"))) - }; -} - -pub(crate) struct Coder { - inner: Framed>, -} - -impl Coder -where - I: AsyncWrite + AsyncRead + Unpin, -{ - pub(crate) fn new(io: I) -> Self { - Self { - inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), - } - } - pub(crate) async fn close(mut self) -> io::Result<()> { - self.inner.close().await?; - Ok(()) - } -} - -impl Coder -where - I: AsyncRead + Unpin, -{ - pub(crate) async fn next(&mut self) -> io::Result - where - proto::Message: TryInto, - io::Error: From, - { - Ok(self.next_msg().await?.try_into()?) - } - - async fn next_msg(&mut self) -> io::Result { - self.inner - .next() - .await - .ok_or(io::Error::new( - ErrorKind::UnexpectedEof, - "no request to read", - ))? - .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) - } -} - -impl Coder -where - I: AsyncWrite + Unpin, -{ - pub(crate) async fn send(&mut self, msg: M) -> io::Result<()> - where - M: Into, - { - self.inner.send(msg.into()).await?; - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub(crate) enum Request { - Dial(DialRequest), - Data(DialDataResponse), -} - -impl From for proto::Message { - fn from(val: DialRequest) -> Self { - let addrs = val.addrs.iter().map(|e| e.to_vec()).collect(); - let nonce = Some(val.nonce); - - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }), - } - } -} - -impl From for proto::Message { - fn from(val: DialDataResponse) -> Self { - debug_assert!( - val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, - "data_count too large" - ); - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(vec![0; val.data_count]), // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI - }), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct DialRequest { - pub(crate) addrs: Vec, - pub(crate) nonce: u64, -} - -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct DialDataResponse { - data_count: usize, -} - -impl DialDataResponse { - pub(crate) fn new(data_count: usize) -> Option { - if data_count <= DATA_FIELD_LEN_UPPER_BOUND { - Some(Self { data_count }) - } else { - None - } - } - - pub(crate) fn get_data_count(&self) -> usize { - self.data_count - } -} - -impl TryFrom for Request { - type Error = io::Error; - - fn try_from(msg: proto::Message) -> Result { - match msg.msg { - proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { - let addrs = addrs - .into_iter() - .map(|e| e.to_vec()) - .map(|e| { - Multiaddr::try_from(e).map_err(|err| { - new_io_invalid_data_err(format!("invalid multiaddr: {}", err)) - }) - }) - .collect::, io::Error>>()?; - let nonce = ok_or_invalid_data!(nonce)?; - Ok(Self::Dial(DialRequest { addrs, nonce })) - } - proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { - let data_count = ok_or_invalid_data!(data)?.len(); - Ok(Self::Data(DialDataResponse { data_count })) - } - _ => Err(new_io_invalid_data_err( - "expected dialResponse or dialDataRequest", - )), - } - } -} - -#[derive(Debug, Clone)] -pub(crate) enum Response { - Dial(DialResponse), - Data(DialDataRequest), -} - -#[derive(Debug, Clone)] -pub(crate) struct DialDataRequest { - pub(crate) addr_idx: usize, - pub(crate) num_bytes: usize, -} - -#[derive(Debug, Clone)] -pub(crate) struct DialResponse { - pub(crate) status: proto::mod_DialResponse::ResponseStatus, - pub(crate) addr_idx: usize, - pub(crate) dial_status: proto::DialStatus, -} - -impl TryFrom for Response { - type Error = io::Error; - - fn try_from(msg: proto::Message) -> Result { - match msg.msg { - proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { - status, - addrIdx, - dialStatus, - }) => { - let status = ok_or_invalid_data!(status)?; - let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; - let dial_status = ok_or_invalid_data!(dialStatus)?; - Ok(Response::Dial(DialResponse { - status, - addr_idx, - dial_status, - })) - } - proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx, - numBytes, - }) => { - let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; - let num_bytes = ok_or_invalid_data!(numBytes)? as usize; - Ok(Self::Data(DialDataRequest { - addr_idx, - num_bytes, - })) - } - _ => Err(new_io_invalid_data_err( - "invalid message type, expected dialResponse or dialDataRequest", - )), - } - } -} - -impl From for proto::Message { - fn from(val: Response) -> Self { - match val { - Response::Dial(DialResponse { - status, - addr_idx, - dial_status, - }) => proto::Message { - msg: proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { - status: Some(status), - addrIdx: Some(addr_idx as u32), - dialStatus: Some(dial_status), - }), - }, - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), - }), - }, - } - } -} - -impl DialDataRequest { - pub(crate) fn from_rng(addr_idx: usize, mut rng: R) -> Self { - let num_bytes = rng.gen_range(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND); - Self { - addr_idx, - num_bytes, - } - } -} - -const DIAL_BACK_MAX_SIZE: usize = 10; - -pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let msg = proto::DialBack { nonce: Some(nonce) }; - let mut framed = FramedWrite::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); - - framed - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - - Ok(()) -} - -pub(crate) async fn recv_dial_back(stream: impl AsyncRead + Unpin) -> io::Result { - let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); - let proto::DialBack { nonce } = framed - .next() - .await - .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - let nonce = ok_or_invalid_data!(nonce)?; - - Ok(nonce) -} - -pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = proto::DialBackResponse { - status: Some(proto::mod_DialBackResponse::DialBackStatus::OK), - }; - let mut framed = FramedWrite::new( - stream, - Codec::::new(DIAL_BACK_MAX_SIZE), - ); - framed - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - framed.close().await?; - - Ok(()) -} - -pub(crate) async fn recv_dial_back_response( - stream: impl AsyncRead + AsyncWrite + Unpin, -) -> io::Result<()> { - let framed = &mut FramedRead::new( - stream, - Codec::::new(DIAL_BACK_MAX_SIZE), - ); - let proto::DialBackResponse { status } = framed - .next() - .await - .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - framed.close().await?; - if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "invalid dial back response", - )) - } -} - -#[cfg(test)] -mod tests { - use crate::v2::generated::structs::{ - mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, - }; - - #[test] - fn message_correct_max_size() { - let message_bytes = quick_protobuf::serialize_into_vec(&Message { - msg: OneOfmsg::dialDataResponse(GenDialDataResponse { - data: Some(vec![0; 4096]), - }), - }) - .unwrap(); - assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); - } - - #[test] - fn dial_back_correct_size() { - let dial_back = super::proto::DialBack { nonce: Some(0) }; - let buf = quick_protobuf::serialize_into_vec(&dial_back).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - - let dial_back_none = super::proto::DialBack { nonce: None }; - let buf = quick_protobuf::serialize_into_vec(&dial_back_none).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - - let dial_back_max_nonce = super::proto::DialBack { - nonce: Some(u64::MAX), - }; - let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - } -} diff --git a/protocols/autonat/src/v2/server.rs b/protocols/autonat/src/v2/server.rs deleted file mode 100644 index 25819307784..00000000000 --- a/protocols/autonat/src/v2/server.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod behaviour; -mod handler; - -pub use behaviour::Behaviour; -pub use behaviour::Event; diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs deleted file mode 100644 index 97446c6263f..00000000000 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::{ - collections::{HashMap, VecDeque}, - io, - task::{Context, Poll}, -}; - -use crate::v2::server::handler::dial_request::DialBackStatus; -use either::Either; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_swarm::dial_opts::PeerCondition; -use libp2p_swarm::{ - dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, - FromSwarm, NetworkBehaviour, ToSwarm, -}; -use rand_core::{OsRng, RngCore}; - -use crate::v2::server::handler::{ - dial_back, - dial_request::{self, DialBackCommand}, - Handler, -}; - -pub struct Behaviour -where - R: Clone + Send + RngCore + 'static, -{ - dialing_dial_back: HashMap, - pending_events: VecDeque< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - >, - rng: R, -} - -impl Default for Behaviour { - fn default() -> Self { - Self::new(OsRng) - } -} - -impl Behaviour -where - R: RngCore + Send + Clone + 'static, -{ - pub fn new(rng: R) -> Self { - Self { - dialing_dial_back: HashMap::new(), - pending_events: VecDeque::new(), - rng, - } - } -} - -impl NetworkBehaviour for Behaviour -where - R: RngCore + Send + Clone + 'static, -{ - type ConnectionHandler = Handler; - - type ToSwarm = Event; - - fn handle_established_inbound_connection( - &mut self, - _connection_id: ConnectionId, - peer: PeerId, - _local_addr: &Multiaddr, - remote_addr: &Multiaddr, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Right(dial_request::Handler::new( - peer, - remote_addr.clone(), - self.rng.clone(), - ))) - } - - fn handle_established_outbound_connection( - &mut self, - connection_id: ConnectionId, - _peer: PeerId, - _addr: &Multiaddr, - _role_override: Endpoint, - _port_use: PortUse, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(match self.dialing_dial_back.remove(&connection_id) { - Some(cmd) => Either::Left(Either::Left(dial_back::Handler::new(cmd))), - None => Either::Left(Either::Right(dummy::ConnectionHandler)), - }) - } - - fn on_swarm_event(&mut self, event: FromSwarm) { - if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { - if let Some(DialBackCommand { back_channel, .. }) = - self.dialing_dial_back.remove(&connection_id) - { - let _ = back_channel.send(Err(DialBackStatus::DialErr)); - } - } - } - - fn on_connection_handler_event( - &mut self, - peer_id: PeerId, - _connection_id: ConnectionId, - event: as ConnectionHandler>::ToBehaviour, - ) { - match event { - Either::Left(Either::Left(Ok(_))) => {} - Either::Left(Either::Left(Err(e))) => { - tracing::debug!("dial back error: {e:?}"); - } - Either::Left(Either::Right(v)) => void::unreachable(v), - Either::Right(Either::Left(cmd)) => { - let addr = cmd.addr.clone(); - let opts = DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr])) - .condition(PeerCondition::Always) - .allocate_new_port() - .build(); - let conn_id = opts.connection_id(); - self.dialing_dial_back.insert(conn_id, cmd); - self.pending_events.push_back(ToSwarm::Dial { opts }); - } - Either::Right(Either::Right(status_update)) => self - .pending_events - .push_back(ToSwarm::GenerateEvent(status_update)), - } - } - - fn poll( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll as ConnectionHandler>::FromBehaviour>> { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - Poll::Pending - } -} - -#[derive(Debug)] -pub struct Event { - /// All address that were submitted for testing. - pub all_addrs: Vec, - /// The address that was eventually tested. - pub tested_addr: Multiaddr, - /// The peer id of the client that submitted addresses for testing. - pub client: PeerId, - /// The amount of data that was requested by the server and was transmitted. - pub data_amount: usize, - /// The result of the test. - pub result: Result<(), io::Error>, -} diff --git a/protocols/autonat/src/v2/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs deleted file mode 100644 index ffdad69c86f..00000000000 --- a/protocols/autonat/src/v2/server/handler.rs +++ /dev/null @@ -1,8 +0,0 @@ -use either::Either; -use libp2p_swarm::dummy; - -pub(crate) mod dial_back; -pub(crate) mod dial_request; - -pub(crate) type Handler = - Either, dial_request::Handler>; diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs deleted file mode 100644 index 3cacd4ff32b..00000000000 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::{ - convert::identity, - io, - task::{Context, Poll}, - time::Duration, -}; - -use futures::{AsyncRead, AsyncWrite}; -use futures_bounded::FuturesSet; -use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; -use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, - SubstreamProtocol, -}; - -use crate::v2::{ - protocol::{dial_back, recv_dial_back_response}, - DIAL_BACK_PROTOCOL, -}; - -use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; - -pub(crate) type ToBehaviour = io::Result<()>; - -pub struct Handler { - pending_nonce: Option, - requested_substream_nonce: Option, - outbound: FuturesSet, -} - -impl Handler { - pub(crate) fn new(cmd: DialBackCommand) -> Self { - Self { - pending_nonce: Some(cmd), - requested_substream_nonce: None, - outbound: FuturesSet::new(Duration::from_secs(10), 5), - } - } -} - -impl ConnectionHandler for Handler { - type FromBehaviour = (); - type ToBehaviour = ToBehaviour; - type InboundProtocol = DeniedUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(DeniedUpgrade, ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - if let Poll::Ready(result) = self.outbound.poll_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - result - .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) - .and_then(identity), - )); - } - if let Some(cmd) = self.pending_nonce.take() { - self.requested_substream_nonce = Some(cmd); - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()), - }); - } - Poll::Pending - } - - fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, .. - }) => { - if let Some(cmd) = self.requested_substream_nonce.take() { - if self - .outbound - .try_push(perform_dial_back(protocol, cmd)) - .is_err() - { - tracing::warn!("Dial back dropped, too many requests in flight"); - } - } else { - tracing::warn!("received dial back substream without nonce"); - } - } - ConnectionEvent::DialUpgradeError(DialUpgradeError { - error: StreamUpgradeError::NegotiationFailed | StreamUpgradeError::Timeout, - .. - }) => { - if let Some(cmd) = self.requested_substream_nonce.take() { - let _ = cmd.back_channel.send(Err(DialBackRes::DialBackErr)); - } - } - _ => {} - } - } -} - -async fn perform_dial_back( - mut stream: impl AsyncRead + AsyncWrite + Unpin, - DialBackCommand { - nonce, - back_channel, - .. - }: DialBackCommand, -) -> io::Result<()> { - let res = dial_back(&mut stream, nonce) - .await - .map_err(|_| DialBackRes::DialBackErr) - .map(|_| ()); - - let res = match res { - Ok(()) => recv_dial_back_response(stream) - .await - .map_err(|_| DialBackRes::DialBackErr) - .map(|_| ()), - Err(e) => Err(e), - }; - back_channel - .send(res) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; - Ok(()) -} diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs deleted file mode 100644 index f8c3f6b654d..00000000000 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ /dev/null @@ -1,332 +0,0 @@ -use std::{ - io, - task::{Context, Poll}, - time::Duration, -}; - -use either::Either; -use futures::{ - channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, SinkExt, StreamExt, -}; -use futures_bounded::FuturesSet; -use libp2p_core::{ - upgrade::{DeniedUpgrade, ReadyUpgrade}, - Multiaddr, -}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, -}; -use rand_core::RngCore; - -use crate::v2::{ - generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, - server::behaviour::Event, - Nonce, DIAL_REQUEST_PROTOCOL, -}; - -#[derive(Debug, PartialEq)] -pub(crate) enum DialBackStatus { - /// Failure during dial - DialErr, - /// Failure during dial back - DialBackErr, -} - -#[derive(Debug)] -pub struct DialBackCommand { - pub(crate) addr: Multiaddr, - pub(crate) nonce: Nonce, - pub(crate) back_channel: oneshot::Sender>, -} - -pub struct Handler { - client_id: PeerId, - observed_multiaddr: Multiaddr, - dial_back_cmd_sender: mpsc::Sender, - dial_back_cmd_receiver: mpsc::Receiver, - inbound: FuturesSet, - rng: R, -} - -impl Handler -where - R: RngCore, -{ - pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { - let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); - Self { - client_id, - observed_multiaddr, - dial_back_cmd_sender, - dial_back_cmd_receiver, - inbound: FuturesSet::new(Duration::from_secs(10), 10), - rng, - } - } -} - -impl ConnectionHandler for Handler -where - R: RngCore + Send + Clone + 'static, -{ - type FromBehaviour = void::Void; - type ToBehaviour = Either; - type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = DeniedUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - loop { - match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(event)) => { - if let Err(e) = &event.result { - tracing::warn!("inbound request handle failed: {:?}", e); - } - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( - event, - ))); - } - Poll::Ready(Err(e)) => { - tracing::warn!("inbound request handle timed out {e:?}"); - } - Poll::Pending => break, - } - } - if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); - } - Poll::Pending - } - - fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - if self - .inbound - .try_push(handle_request( - protocol, - self.observed_multiaddr.clone(), - self.client_id, - self.dial_back_cmd_sender.clone(), - self.rng.clone(), - )) - .is_err() - { - tracing::warn!( - "failed to push inbound request handler, too many requests in flight" - ); - } - } - ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { - tracing::debug!("inbound request failed: {:?}", error); - } - _ => {} - } - } -} - -enum HandleFail { - InternalError(usize), - RequestRejected, - DialRefused, - DialBack { - idx: usize, - result: Result<(), DialBackStatus>, - }, -} - -impl From for DialResponse { - fn from(value: HandleFail) -> Self { - match value { - HandleFail::InternalError(addr_idx) => Self { - status: ResponseStatus::E_INTERNAL_ERROR, - addr_idx, - dial_status: DialStatus::UNUSED, - }, - HandleFail::RequestRejected => Self { - status: ResponseStatus::E_REQUEST_REJECTED, - addr_idx: 0, - dial_status: DialStatus::UNUSED, - }, - HandleFail::DialRefused => Self { - status: ResponseStatus::E_DIAL_REFUSED, - addr_idx: 0, - dial_status: DialStatus::UNUSED, - }, - HandleFail::DialBack { idx, result } => Self { - status: ResponseStatus::OK, - addr_idx: idx, - dial_status: match result { - Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, - Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, - Ok(()) => DialStatus::OK, - }, - }, - } - } -} - -async fn handle_request( - stream: impl AsyncRead + AsyncWrite + Unpin, - observed_multiaddr: Multiaddr, - client: PeerId, - dial_back_cmd_sender: mpsc::Sender, - rng: impl RngCore, -) -> Event { - let mut coder = Coder::new(stream); - let mut all_addrs = Vec::new(); - let mut tested_addr_opt = None; - let mut data_amount = 0; - let response = handle_request_internal( - &mut coder, - observed_multiaddr.clone(), - dial_back_cmd_sender, - rng, - &mut all_addrs, - &mut tested_addr_opt, - &mut data_amount, - ) - .await - .unwrap_or_else(|e| e.into()); - let Some(tested_addr) = tested_addr_opt else { - return Event { - all_addrs, - tested_addr: observed_multiaddr, - client, - data_amount, - result: Err(io::Error::new( - io::ErrorKind::Other, - "client is not conformint to protocol. the tested address is not the observed address", - )), - }; - }; - if let Err(e) = coder.send(Response::Dial(response)).await { - return Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Err(e), - }; - } - if let Err(e) = coder.close().await { - return Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Err(e), - }; - } - Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - } -} - -async fn handle_request_internal( - coder: &mut Coder, - observed_multiaddr: Multiaddr, - dial_back_cmd_sender: mpsc::Sender, - mut rng: impl RngCore, - all_addrs: &mut Vec, - tested_addrs: &mut Option, - data_amount: &mut usize, -) -> Result -where - I: AsyncRead + AsyncWrite + Unpin, -{ - let DialRequest { mut addrs, nonce } = match coder - .next() - .await - .map_err(|_| HandleFail::InternalError(0))? - { - Request::Dial(dial_request) => dial_request, - Request::Data(_) => { - return Err(HandleFail::RequestRejected); - } - }; - *all_addrs = addrs.clone(); - let idx = 0; - let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; - *tested_addrs = Some(addr.clone()); - *data_amount = 0; - if addr != observed_multiaddr { - let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); - let mut rem_data = dial_data_request.num_bytes; - coder - .send(Response::Data(dial_data_request)) - .await - .map_err(|_| HandleFail::InternalError(idx))?; - while rem_data > 0 { - let data_count = match coder - .next() - .await - .map_err(|_e| HandleFail::InternalError(idx))? - { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => dial_data_response.get_data_count(), - }; - rem_data = rem_data.saturating_sub(data_count); - *data_amount += data_count; - } - } - let (back_channel, rx) = oneshot::channel(); - let dial_back_cmd = DialBackCommand { - addr, - nonce, - back_channel, - }; - dial_back_cmd_sender - .clone() - .send(dial_back_cmd) - .await - .map_err(|_| HandleFail::DialBack { - idx, - result: Err(DialBackStatus::DialErr), - })?; - - let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; - if let Err(err) = dial_back { - return Err(HandleFail::DialBack { - idx, - result: Err(err), - }); - } - Ok(DialResponse { - status: ResponseStatus::OK, - addr_idx: idx, - dial_status: DialStatus::OK, - }) -} diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs deleted file mode 100644 index abd0c4bd8eb..00000000000 --- a/protocols/autonat/tests/autonatv2.rs +++ /dev/null @@ -1,568 +0,0 @@ -use libp2p_autonat::v2::client::{self, Config}; -use libp2p_autonat::v2::server; -use libp2p_core::transport::TransportError; -use libp2p_core::Multiaddr; -use libp2p_swarm::{ - DialError, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, Swarm, SwarmEvent, -}; -use libp2p_swarm_test::SwarmExt; -use rand_core::OsRng; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::oneshot; -use tracing_subscriber::EnvFilter; - -#[tokio::test] -async fn confirm_successful() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - let (mut alice, mut bob) = start_and_connect().await; - - let cor_server_peer = *alice.local_peer_id(); - let cor_client_peer = *bob.local_peer_id(); - let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); - let alice_bob_external_addrs = bob_external_addrs.clone(); - - let alice_task = async { - let _ = alice - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), - _ => None, - }) - .await; - - let (dialed_peer_id, dialed_connection_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - .. - } => peer_id.map(|peer_id| (peer_id, connection_id)), - _ => None, - }) - .await; - - assert_eq!(dialed_peer_id, cor_client_peer); - - let _ = alice - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - peer_id, - connection_id, - .. - } if peer_id == dialed_peer_id - && peer_id == cor_client_peer - && connection_id == dialed_connection_id => - { - Some(()) - } - _ => None, - }) - .await; - - let server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result, - } = alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { - Some(status_update) - } - _ => None, - }) - .await; - - assert_eq!(tested_addr, bob_external_addrs.first().cloned().unwrap()); - assert_eq!(data_amount, 0); - assert_eq!(client, cor_client_peer); - assert_eq!(&all_addrs[..], &bob_external_addrs[..]); - assert!(result.is_ok(), "Result: {result:?}"); - }; - - let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; - let incoming_conn_id = bob - .wait(|event| match event { - SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), - _ => None, - }) - .await; - - let _ = bob - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }) - .await; - - let client::Event { - tested_addr, - bytes_sent, - server, - result, - } = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { - Some(status_update) - } - _ => None, - }) - .await; - assert_eq!( - tested_addr, - alice_bob_external_addrs.first().cloned().unwrap() - ); - assert_eq!(bytes_sent, 0); - assert_eq!(server, cor_server_peer); - assert!(result.is_ok(), "Result is {result:?}"); - }; - - tokio::join!(alice_task, bob_task); -} - -#[tokio::test] -async fn dial_back_to_unsupported_protocol() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - let (mut alice, mut bob) = bootstrap().await; - - let alice_peer_id = *alice.local_peer_id(); - - let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); - let bob_test_addr = test_addr.clone(); - bob.behaviour_mut() - .autonat - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &test_addr }, - )); - - let (bob_done_tx, bob_done_rx) = oneshot::channel(); - - let alice_task = async { - let (alice_dialing_peer, alice_conn_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - } => peer_id.map(|e| (e, connection_id)), - _ => None, - }) - .await; - let mut outgoing_conn_error = alice - .wait(|event| match event { - SwarmEvent::OutgoingConnectionError { - connection_id, - peer_id: Some(peer_id), - error: DialError::Transport(transport_errs), - } if connection_id == alice_conn_id && alice_dialing_peer == peer_id => { - Some(transport_errs) - } - _ => None, - }) - .await; - if let Some((multiaddr, TransportError::MultiaddrNotSupported(not_supported_addr))) = - outgoing_conn_error.pop() - { - assert_eq!( - multiaddr, - test_addr.clone().with_p2p(alice_dialing_peer).unwrap() - ); - assert_eq!(not_supported_addr, multiaddr,); - } else { - panic!("Peers are empty"); - } - assert_eq!(outgoing_conn_error.len(), 0); - let data_amount = alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - })) if all_addrs == vec![test_addr.clone()] - && tested_addr == test_addr.clone() - && client == alice_dialing_peer => - { - Some(data_amount) - } - _ => None, - }) - .await; - - let handler = tokio::spawn(async move { - alice.loop_on_next().await; - }); - let _ = bob_done_rx.await; - handler.abort(); - data_amount - }; - - let bob_task = async { - let data_amount = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(bytes_sent), - _ => None, - }) - .await; - bob_done_tx.send(()).unwrap(); - data_amount - }; - let (alice_amount, bob_amount) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_amount, bob_amount); -} - -#[tokio::test] -async fn dial_back_to_non_libp2p() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - let (mut alice, mut bob) = bootstrap().await; - let alice_peer_id = *alice.local_peer_id(); - - for addr_str in ["/ip4/169.150.247.38/tcp/32", "/ip6/::1/tcp/1000"] { - let addr: Multiaddr = addr_str.parse().unwrap(); - let bob_addr = addr.clone(); - bob.behaviour_mut() - .autonat - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, - )); - - let alice_task = async { - let (alice_dialing_peer, alice_conn_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - } => peer_id.map(|p| (p, connection_id)), - _ => None, - }) - .await; - let mut outgoing_conn_error = alice - .wait(|event| match event { - SwarmEvent::OutgoingConnectionError { - connection_id, - peer_id: Some(peer_id), - error: DialError::Transport(peers), - } if connection_id == alice_conn_id && peer_id == alice_dialing_peer => { - Some(peers) - } - _ => None, - }) - .await; - - if let Some((multiaddr, TransportError::Other(o))) = outgoing_conn_error.pop() { - assert_eq!( - multiaddr, - addr.clone().with_p2p(alice_dialing_peer).unwrap() - ); - let error_string = o.to_string(); - assert!( - error_string.contains("Connection refused"), - "Correct error string: {error_string} for {addr_str}" - ); - } else { - panic!("No outgoing connection errors"); - } - - alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - })) if all_addrs == vec![addr.clone()] - && tested_addr == addr - && alice_dialing_peer == client => - { - Some(data_amount) - } - _ => None, - }) - .await - }; - let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), - _ => None, - }) - .await - }; - - let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_bytes_sent, bob_bytes_sent); - bob.behaviour_mut().autonat.validate_addr(&addr); - } -} - -#[tokio::test] -async fn dial_back_to_not_supporting() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - - let (mut alice, mut bob) = bootstrap().await; - let alice_peer_id = *alice.local_peer_id(); - - let (bob_done_tx, bob_done_rx) = oneshot::channel(); - - let hannes = new_dummy().await; - let hannes_peer_id = *hannes.local_peer_id(); - let unreachable_address = hannes.external_addresses().next().unwrap().clone(); - let bob_unreachable_address = unreachable_address.clone(); - bob.behaviour_mut() - .autonat - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { - addr: &unreachable_address, - }, - )); - - let handler = tokio::spawn(async { hannes.loop_on_next().await }); - - let alice_task = async { - let (alice_dialing_peer, alice_conn_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - } => peer_id.map(|p| (p, connection_id)), - _ => None, - }) - .await; - alice - .wait(|event| match event { - SwarmEvent::OutgoingConnectionError { - connection_id, - peer_id: Some(peer_id), - error: DialError::WrongPeerId { obtained, .. }, - } if connection_id == alice_conn_id - && peer_id == alice_dialing_peer - && obtained == hannes_peer_id => - { - Some(()) - } - _ => None, - }) - .await; - - let data_amount = alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - })) if all_addrs == vec![unreachable_address.clone()] - && tested_addr == unreachable_address - && alice_dialing_peer == client => - { - Some(data_amount) - } - _ => None, - }) - .await; - tokio::select! { - _ = bob_done_rx => { - data_amount - } - _ = alice.loop_on_next() => { - unreachable!(); - } - } - }; - - let bob_task = async { - let bytes_sent = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { - Some(bytes_sent) - } - _ => None, - }) - .await; - bob_done_tx.send(()).unwrap(); - bytes_sent - }; - - let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_bytes_sent, bob_bytes_sent); - handler.abort(); -} - -async fn new_server() -> Swarm { - let mut node = Swarm::new_ephemeral(|identity| CombinedServer { - autonat: libp2p_autonat::v2::server::Behaviour::default(), - identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( - "/libp2p-test/1.0.0".into(), - identity.public().clone(), - )), - }); - node.listen().with_tcp_addr_external().await; - - node -} - -async fn new_client() -> Swarm { - let mut node = Swarm::new_ephemeral(|identity| CombinedClient { - autonat: libp2p_autonat::v2::client::Behaviour::new( - OsRng, - Config::default().with_probe_interval(Duration::from_millis(100)), - ), - identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( - "/libp2p-test/1.0.0".into(), - identity.public().clone(), - )), - }); - node.listen().with_tcp_addr_external().await; - node -} - -#[derive(libp2p_swarm::NetworkBehaviour)] -#[behaviour(prelude = "libp2p_swarm::derive_prelude")] -struct CombinedServer { - autonat: libp2p_autonat::v2::server::Behaviour, - identify: libp2p_identify::Behaviour, -} - -#[derive(libp2p_swarm::NetworkBehaviour)] -#[behaviour(prelude = "libp2p_swarm::derive_prelude")] -struct CombinedClient { - autonat: libp2p_autonat::v2::client::Behaviour, - identify: libp2p_identify::Behaviour, -} - -async fn new_dummy() -> Swarm { - let mut node = Swarm::new_ephemeral(|identity| { - libp2p_identify::Behaviour::new(libp2p_identify::Config::new( - "/libp2p-test/1.0.0".into(), - identity.public().clone(), - )) - }); - node.listen().with_tcp_addr_external().await; - node -} - -async fn start_and_connect() -> (Swarm, Swarm) { - let mut alice = new_server().await; - let mut bob = new_client().await; - - bob.connect(&mut alice).await; - (alice, bob) -} - -async fn bootstrap() -> (Swarm, Swarm) { - let (mut alice, mut bob) = start_and_connect().await; - - let cor_server_peer = *alice.local_peer_id(); - let cor_client_peer = *bob.local_peer_id(); - - let alice_task = async { - let _ = alice - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), - _ => None, - }) - .await; - - let (dialed_peer_id, dialed_connection_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - .. - } => peer_id.map(|peer_id| (peer_id, connection_id)), - _ => None, - }) - .await; - - let _ = alice - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - peer_id, - connection_id, - .. - } if peer_id == dialed_peer_id - && peer_id == cor_client_peer - && connection_id == dialed_connection_id => - { - Some(()) - } - _ => None, - }) - .await; - - alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(_)) => Some(()), - _ => None, - }) - .await; - }; - - let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; - let incoming_conn_id = bob - .wait(|event| match event { - SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), - _ => None, - }) - .await; - - let _ = bob - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }) - .await; - - bob.wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), - _ => None, - }) - .await; - }; - - tokio::join!(alice_task, bob_task); - (alice, bob) -} diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 2b1460cac85..7509d3ef425 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -17,7 +17,6 @@ // 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. -#![allow(deprecated)] use async_std::task::JoinHandle; use libp2p_autonat::{ diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index a01c5901b8e..fd97b1a9132 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -17,7 +17,6 @@ // 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. -#![allow(deprecated)] use libp2p_autonat::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, From 0918ea84f48b462f041fc76b2bdf68232e30a7ad Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 May 2024 13:17:39 +0200 Subject: [PATCH 96/97] Remove deprecated --- misc/server/src/behaviour.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index c5bcb7e9d9a..36b18c9798d 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -1,5 +1,3 @@ -#![allow(deprecated)] - use libp2p::autonat; use libp2p::identify; use libp2p::kad; From 45863c3ab503c716ea03bc5eb563ce923b29fdec Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 May 2024 13:23:20 +0200 Subject: [PATCH 97/97] Formatting --- protocols/autonat/src/behaviour.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index bf36080a721..a1c0fe968f0 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -517,8 +517,13 @@ impl NetworkBehaviour for Behaviour { role_override: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_swarm_event(&mut self, event: FromSwarm) {