fix(log): Stop logging peer IP addresses, to protect user privacy (#6662)

* Add a PeerSocketAddr type which hides its IP address, but shows the port

* Manually replace SocketAddr with PeerSocketAddr where needed

```sh
fastmod SocketAddr PeerSocketAddr zebra-network
```

* Add missing imports

* Make converting into PeerSocketAddr easier

* Fix some unused imports

* Add a canonical_peer_addr() function

* Fix connection handling for PeerSocketAddr

* Fix serialization for PeerSocketAddr

* Fix tests for PeerSocketAddr

* Remove some unused imports

* Fix address book listener handling

* Remove redundant imports and conversions

* Update outdated IPv4-mapped IPv6 address code

* Make addresses canonical when deserializing

* Stop logging peer addresses in RPC code

* Update zebrad tests with new PeerSocketAddr type

* Update zebra-rpc tests with new PeerSocketAddr type

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
teor 2023-05-15 01:06:07 +10:00 committed by GitHub
parent 989c5979fb
commit b0d9471214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 447 additions and 364 deletions

View File

@ -17,9 +17,13 @@ use tracing::Span;
use zebra_chain::parameters::Network;
use crate::{
constants, meta_addr::MetaAddrChange, protocol::external::canonical_socket_addr,
types::MetaAddr, AddressBookPeers, PeerAddrState,
constants,
meta_addr::MetaAddrChange,
protocol::external::{canonical_peer_addr, canonical_socket_addr},
types::MetaAddr,
AddressBookPeers, PeerAddrState, PeerSocketAddr,
};
#[cfg(test)]
mod tests;
@ -66,7 +70,7 @@ pub struct AddressBook {
/// We reverse the comparison order, because the standard library
/// ([`BTreeMap`](std::collections::BTreeMap)) sorts in ascending order, but
/// [`OrderedMap`] sorts in descending order.
by_addr: OrderedMap<SocketAddr, MetaAddr, Reverse<MetaAddr>>,
by_addr: OrderedMap<PeerSocketAddr, MetaAddr, Reverse<MetaAddr>>,
/// The local listener address.
local_listener: SocketAddr,
@ -182,7 +186,7 @@ impl AddressBook {
let addrs = addrs
.into_iter()
.map(|mut meta_addr| {
meta_addr.addr = canonical_socket_addr(meta_addr.addr);
meta_addr.addr = canonical_peer_addr(meta_addr.addr);
meta_addr
})
.filter(|meta_addr| meta_addr.address_is_valid_for_outbound(network))
@ -219,11 +223,16 @@ impl AddressBook {
///
/// This address contains minimal state, but it is not sanitized.
pub fn local_listener_meta_addr(&self) -> MetaAddr {
MetaAddr::new_local_listener_change(&self.local_listener)
MetaAddr::new_local_listener_change(self.local_listener)
.into_new_meta_addr()
.expect("unexpected invalid new local listener addr")
}
/// Get the local listener [`SocketAddr`].
pub fn local_listener_socket_addr(&self) -> SocketAddr {
self.local_listener
}
/// Get the contents of `self` in random order with sanitized timestamps.
pub fn sanitized(&self, now: chrono::DateTime<Utc>) -> Vec<MetaAddr> {
use rand::seq::SliceRandom;
@ -257,8 +266,8 @@ impl AddressBook {
/// Look up `addr` in the address book, and return its [`MetaAddr`].
///
/// Converts `addr` to a canonical address before looking it up.
pub fn get(&mut self, addr: &SocketAddr) -> Option<MetaAddr> {
let addr = canonical_socket_addr(*addr);
pub fn get(&mut self, addr: PeerSocketAddr) -> Option<MetaAddr> {
let addr = canonical_peer_addr(*addr);
// Unfortunately, `OrderedMap` doesn't implement `get`.
let meta_addr = self.by_addr.remove(&addr);
@ -278,7 +287,7 @@ impl AddressBook {
/// All changes should go through `update`, so that the address book
/// only contains valid outbound addresses.
///
/// Change addresses must be canonical `SocketAddr`s. This makes sure that
/// Change addresses must be canonical `PeerSocketAddr`s. This makes sure that
/// each address book entry has a unique IP address.
///
/// # Security
@ -287,11 +296,11 @@ impl AddressBook {
/// to the address book. This prevents rapid reconnections to the same peer.
///
/// As an exception, this function can ignore all changes for specific
/// [`SocketAddr`]s. Ignored addresses will never be used to connect to
/// [`PeerSocketAddr`]s. Ignored addresses will never be used to connect to
/// peers.
#[allow(clippy::unwrap_in_result)]
pub fn update(&mut self, change: MetaAddrChange) -> Option<MetaAddr> {
let previous = self.get(&change.addr());
let previous = self.get(change.addr());
let _guard = self.span.enter();
@ -378,7 +387,7 @@ impl AddressBook {
/// All address removals should go through `take`, so that the address
/// book metrics are accurate.
#[allow(dead_code)]
fn take(&mut self, removed_addr: SocketAddr) -> Option<MetaAddr> {
fn take(&mut self, removed_addr: PeerSocketAddr) -> Option<MetaAddr> {
let _guard = self.span.enter();
let instant_now = Instant::now();
@ -399,9 +408,9 @@ impl AddressBook {
}
}
/// Returns true if the given [`SocketAddr`] is pending a reconnection
/// Returns true if the given [`PeerSocketAddr`] is pending a reconnection
/// attempt.
pub fn pending_reconnection_addr(&mut self, addr: &SocketAddr) -> bool {
pub fn pending_reconnection_addr(&mut self, addr: PeerSocketAddr) -> bool {
let meta_addr = self.get(addr);
let _guard = self.span.enter();

View File

@ -17,8 +17,8 @@ use crate::{
DEFAULT_CRAWL_NEW_PEER_INTERVAL, DNS_LOOKUP_TIMEOUT, INBOUND_PEER_LIMIT_MULTIPLIER,
OUTBOUND_PEER_LIMIT_MULTIPLIER,
},
protocol::external::canonical_socket_addr,
BoxError,
protocol::external::{canonical_peer_addr, canonical_socket_addr},
BoxError, PeerSocketAddr,
};
#[cfg(test)]
@ -145,7 +145,7 @@ impl Config {
}
/// Resolve initial seed peer IP addresses, based on the configured network.
pub async fn initial_peers(&self) -> HashSet<SocketAddr> {
pub async fn initial_peers(&self) -> HashSet<PeerSocketAddr> {
Config::resolve_peers(&self.initial_peer_hostnames().iter().cloned().collect()).await
}
@ -154,7 +154,7 @@ impl Config {
///
/// If DNS resolution fails or times out for all peers, continues retrying
/// until at least one peer is found.
async fn resolve_peers(peers: &HashSet<String>) -> HashSet<SocketAddr> {
async fn resolve_peers(peers: &HashSet<String>) -> HashSet<PeerSocketAddr> {
use futures::stream::StreamExt;
if peers.is_empty() {
@ -196,7 +196,7 @@ impl Config {
/// `max_retries` times.
///
/// If DNS continues to fail, returns an empty list of addresses.
async fn resolve_host(host: &str, max_retries: usize) -> HashSet<SocketAddr> {
async fn resolve_host(host: &str, max_retries: usize) -> HashSet<PeerSocketAddr> {
for retries in 0..=max_retries {
if let Ok(addresses) = Config::resolve_host_once(host).await {
return addresses;
@ -225,13 +225,13 @@ impl Config {
///
/// If `host` is a DNS name, performs DNS resolution with a timeout of a few seconds.
/// If DNS resolution fails or times out, returns an error.
async fn resolve_host_once(host: &str) -> Result<HashSet<SocketAddr>, BoxError> {
async fn resolve_host_once(host: &str) -> Result<HashSet<PeerSocketAddr>, BoxError> {
let fut = tokio::net::lookup_host(host);
let fut = tokio::time::timeout(DNS_LOOKUP_TIMEOUT, fut);
match fut.await {
Ok(Ok(ip_addrs)) => {
let ip_addrs: Vec<SocketAddr> = ip_addrs.map(canonical_socket_addr).collect();
let ip_addrs: Vec<PeerSocketAddr> = ip_addrs.map(canonical_peer_addr).collect();
// if we're logging at debug level,
// the full list of IP addresses will be shown in the log message

View File

@ -1,6 +1,6 @@
//! Creating isolated connections to specific peers.
use std::{future::Future, net::SocketAddr};
use std::future::Future;
use futures::future::TryFutureExt;
use tokio::io::{AsyncRead, AsyncWrite};
@ -11,7 +11,7 @@ use zebra_chain::{chain_tip::NoChainTip, parameters::Network};
use crate::{
peer::{self, Client, ConnectedAddr, HandshakeRequest},
peer_set::ActiveConnectionCounter,
BoxError, Config, Request, Response,
BoxError, Config, PeerSocketAddr, Request, Response,
};
// Wait until `arti-client`'s dependency `x25519-dalek v1.2.0` is updated to a higher version. (#5492)
@ -126,7 +126,7 @@ where
/// Prefer `connect_isolated_tor` if available.
pub fn connect_isolated_tcp_direct(
network: Network,
addr: SocketAddr,
addr: impl Into<PeerSocketAddr>,
user_agent: String,
) -> impl Future<Output = Result<Client, BoxError>> {
let nil_inbound_service =
@ -146,7 +146,7 @@ pub fn connect_isolated_tcp_direct(
/// which makes it stand out from other isolated connections from other peers.
pub fn connect_isolated_tcp_direct_with_inbound<InboundService>(
network: Network,
addr: SocketAddr,
addr: impl Into<PeerSocketAddr>,
user_agent: String,
inbound_service: InboundService,
) -> impl Future<Output = Result<Client, BoxError>>
@ -155,7 +155,9 @@ where
Service<Request, Response = Response, Error = BoxError> + Clone + Send + 'static,
InboundService::Future: Send,
{
tokio::net::TcpStream::connect(addr)
let addr = addr.into();
tokio::net::TcpStream::connect(*addr)
.err_into()
.and_then(move |tcp_stream| {
connect_isolated_with_inbound(network, tcp_stream, user_agent, inbound_service)

View File

@ -1,6 +1,6 @@
//! Fixed test vectors for isolated Zebra connections.
use std::{net::SocketAddr, task::Poll, time::Duration};
use std::{task::Poll, time::Duration};
use futures::stream::StreamExt;
use tokio_util::codec::Framed;
@ -151,7 +151,7 @@ async fn check_version_message<PeerTransport>(
//
// SECURITY TODO: check if the timestamp field can be zeroed, to remove another distinguisher (#3300)
let mut fixed_isolated_addr: SocketAddr = "0.0.0.0:0".parse().unwrap();
let mut fixed_isolated_addr: PeerSocketAddr = "0.0.0.0:0".parse().unwrap();
fixed_isolated_addr.set_port(network.default_port());
// Required fields should be accurate and match most other peers.

View File

@ -166,8 +166,9 @@ pub use crate::isolated::tor::connect_isolated_tor;
pub use crate::isolated::tor::connect_isolated_tor_with_inbound;
#[cfg(any(test, feature = "proptest-impl"))]
pub use crate::isolated::{
connect_isolated_tcp_direct_with_inbound, connect_isolated_with_inbound,
pub use crate::{
isolated::{connect_isolated_tcp_direct_with_inbound, connect_isolated_with_inbound},
protocol::external::canonical_peer_addr,
};
pub use crate::{
@ -175,7 +176,7 @@ pub use crate::{
address_book_peers::AddressBookPeers,
config::Config,
isolated::{connect_isolated, connect_isolated_tcp_direct},
meta_addr::PeerAddrState,
meta_addr::{PeerAddrState, PeerSocketAddr},
peer::{Client, ConnectedAddr, ConnectionInfo, HandshakeError, PeerError, SharedPeerError},
peer_set::init,
policies::RetryLimit,

View File

@ -2,7 +2,6 @@
use std::{
cmp::{Ord, Ordering},
net::SocketAddr,
time::Instant,
};
@ -12,18 +11,22 @@ use zebra_chain::{parameters::Network, serialization::DateTime32};
use crate::{
constants,
peer::PeerPreference,
protocol::{external::canonical_socket_addr, types::PeerServices},
peer::{address_is_valid_for_outbound_connections, PeerPreference},
protocol::{external::canonical_peer_addr, types::PeerServices},
};
use MetaAddrChange::*;
use PeerAddrState::*;
pub mod peer_addr;
pub use peer_addr::PeerSocketAddr;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
use crate::protocol::external::arbitrary::canonical_socket_addr_strategy;
use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
#[cfg(any(test, feature = "proptest-impl"))]
pub(crate) mod arbitrary;
@ -134,12 +137,12 @@ pub struct MetaAddr {
/// The peer's canonical socket address.
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
//
// TODO: make addr private, so the constructors can make sure it is a
// canonical SocketAddr (#2357)
pub(crate) addr: SocketAddr,
pub(crate) addr: PeerSocketAddr,
/// The services advertised by the peer.
///
@ -196,18 +199,18 @@ pub enum MetaAddrChange {
NewInitial {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
},
/// Creates a new gossiped `MetaAddr`.
NewGossiped {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
untrusted_services: PeerServices,
untrusted_last_seen: DateTime32,
},
@ -218,9 +221,9 @@ pub enum MetaAddrChange {
NewAlternate {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
untrusted_services: PeerServices,
},
@ -228,9 +231,9 @@ pub enum MetaAddrChange {
NewLocal {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
},
/// Updates an existing `MetaAddr` when an outbound connection attempt
@ -238,18 +241,18 @@ pub enum MetaAddrChange {
UpdateAttempt {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
},
/// Updates an existing `MetaAddr` when a peer responds with a message.
UpdateResponded {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
services: PeerServices,
},
@ -257,9 +260,9 @@ pub enum MetaAddrChange {
UpdateFailed {
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "canonical_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
addr: SocketAddr,
addr: PeerSocketAddr,
services: Option<PeerServices>,
},
}
@ -267,21 +270,21 @@ pub enum MetaAddrChange {
impl MetaAddr {
/// Returns a [`MetaAddrChange::NewInitial`] for a peer that was excluded from
/// the list of the initial peers.
pub fn new_initial_peer(addr: SocketAddr) -> MetaAddrChange {
pub fn new_initial_peer(addr: PeerSocketAddr) -> MetaAddrChange {
NewInitial {
addr: canonical_socket_addr(addr),
addr: canonical_peer_addr(addr),
}
}
/// Returns a new `MetaAddr`, based on the deserialized fields from a
/// gossiped peer [`Addr`][crate::protocol::external::Message::Addr] message.
pub fn new_gossiped_meta_addr(
addr: SocketAddr,
addr: PeerSocketAddr,
untrusted_services: PeerServices,
untrusted_last_seen: DateTime32,
) -> MetaAddr {
MetaAddr {
addr: canonical_socket_addr(addr),
addr: canonical_peer_addr(addr),
services: Some(untrusted_services),
untrusted_last_seen: Some(untrusted_last_seen),
last_response: None,
@ -300,7 +303,7 @@ impl MetaAddr {
let untrusted_services = self.services?;
Some(NewGossiped {
addr: canonical_socket_addr(self.addr),
addr: canonical_peer_addr(self.addr),
untrusted_services,
untrusted_last_seen: self
.untrusted_last_seen
@ -320,52 +323,55 @@ impl MetaAddr {
/// - malicious peers could interfere with other peers' [`AddressBook`](crate::AddressBook) state,
/// or
/// - Zebra could advertise unreachable addresses to its own peers.
pub fn new_responded(addr: &SocketAddr, services: &PeerServices) -> MetaAddrChange {
pub fn new_responded(addr: PeerSocketAddr, services: &PeerServices) -> MetaAddrChange {
UpdateResponded {
addr: canonical_socket_addr(*addr),
addr: canonical_peer_addr(*addr),
services: *services,
}
}
/// Returns a [`MetaAddrChange::UpdateAttempt`] for a peer that we
/// want to make an outbound connection to.
pub fn new_reconnect(addr: &SocketAddr) -> MetaAddrChange {
pub fn new_reconnect(addr: PeerSocketAddr) -> MetaAddrChange {
UpdateAttempt {
addr: canonical_socket_addr(*addr),
addr: canonical_peer_addr(*addr),
}
}
/// Returns a [`MetaAddrChange::NewAlternate`] for a peer's alternate address,
/// received via a `Version` message.
pub fn new_alternate(addr: &SocketAddr, untrusted_services: &PeerServices) -> MetaAddrChange {
pub fn new_alternate(
addr: PeerSocketAddr,
untrusted_services: &PeerServices,
) -> MetaAddrChange {
NewAlternate {
addr: canonical_socket_addr(*addr),
addr: canonical_peer_addr(*addr),
untrusted_services: *untrusted_services,
}
}
/// Returns a [`MetaAddrChange::NewLocal`] for our own listener address.
pub fn new_local_listener_change(addr: &SocketAddr) -> MetaAddrChange {
pub fn new_local_listener_change(addr: impl Into<PeerSocketAddr>) -> MetaAddrChange {
NewLocal {
addr: canonical_socket_addr(*addr),
addr: canonical_peer_addr(addr),
}
}
/// Returns a [`MetaAddrChange::UpdateFailed`] for a peer that has just had
/// an error.
pub fn new_errored(
addr: &SocketAddr,
addr: PeerSocketAddr,
services: impl Into<Option<PeerServices>>,
) -> MetaAddrChange {
UpdateFailed {
addr: canonical_socket_addr(*addr),
addr: canonical_peer_addr(*addr),
services: services.into(),
}
}
/// Create a new `MetaAddr` for a peer that has just shut down.
pub fn new_shutdown(
addr: &SocketAddr,
addr: PeerSocketAddr,
services: impl Into<Option<PeerServices>>,
) -> MetaAddrChange {
// TODO: if the peer shut down in the Responded state, preserve that
@ -374,13 +380,13 @@ impl MetaAddr {
}
/// Return the address for this `MetaAddr`.
pub fn addr(&self) -> SocketAddr {
pub fn addr(&self) -> PeerSocketAddr {
self.addr
}
/// Return the address preference level for this `MetaAddr`.
pub fn peer_preference(&self) -> Result<PeerPreference, &'static str> {
PeerPreference::new(&self.addr, None)
PeerPreference::new(self.addr, None)
}
/// Returns the time of the last successful interaction with this peer.
@ -535,13 +541,13 @@ impl MetaAddr {
&& self.is_probably_reachable(chrono_now)
}
/// Is the [`SocketAddr`] we have for this peer valid for outbound
/// Is the [`PeerSocketAddr`] we have for this peer valid for outbound
/// connections?
///
/// Since the addresses in the address book are unique, this check can be
/// used to permanently reject entire [`MetaAddr`]s.
pub fn address_is_valid_for_outbound(&self, network: Network) -> bool {
PeerPreference::new(&self.addr, network).is_ok()
address_is_valid_for_outbound_connections(self.addr, network).is_ok()
}
/// Is the last known information for this peer valid for outbound
@ -611,7 +617,7 @@ impl MetaAddr {
.expect("unexpected underflow: rem_euclid is strictly less than timestamp");
Some(MetaAddr {
addr: canonical_socket_addr(self.addr),
addr: canonical_peer_addr(self.addr),
// initial peers are sanitized assuming they are `NODE_NETWORK`
// TODO: split untrusted and direct services
// consider sanitizing untrusted services to NODE_NETWORK (#2324)
@ -640,7 +646,7 @@ impl MetaAddr {
impl MetaAddrChange {
/// Return the address for this change.
pub fn addr(&self) -> SocketAddr {
pub fn addr(&self) -> PeerSocketAddr {
match self {
NewInitial { addr }
| NewGossiped { addr, .. }
@ -656,7 +662,7 @@ impl MetaAddrChange {
/// Set the address for this change to `new_addr`.
///
/// This method should only be used in tests.
pub fn set_addr(&mut self, new_addr: SocketAddr) {
pub fn set_addr(&mut self, new_addr: PeerSocketAddr) {
match self {
NewInitial { addr }
| NewGossiped { addr, .. }

View File

@ -1,14 +1,12 @@
//! Randomised test data generation for MetaAddr.
use std::net::SocketAddr;
use proptest::{arbitrary::any, collection::vec, prelude::*};
use zebra_chain::{parameters::Network::*, serialization::DateTime32};
use crate::protocol::external::arbitrary::canonical_socket_addr_strategy;
use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
use super::{MetaAddr, MetaAddrChange, PeerServices};
use super::{MetaAddr, MetaAddrChange, PeerServices, PeerSocketAddr};
/// The largest number of random changes we want to apply to a [`MetaAddr`].
///
@ -36,7 +34,7 @@ impl MetaAddr {
/// [1]: super::PeerAddrState::NeverAttemptedGossiped
pub fn gossiped_strategy() -> BoxedStrategy<Self> {
(
canonical_socket_addr_strategy(),
canonical_peer_addr_strategy(),
any::<PeerServices>(),
any::<DateTime32>(),
)
@ -51,9 +49,9 @@ impl MetaAddr {
///
/// [1]: super::PeerAddrState::NeverAttemptedAlternate
pub fn alternate_strategy() -> BoxedStrategy<Self> {
(canonical_socket_addr_strategy(), any::<PeerServices>())
(canonical_peer_addr_strategy(), any::<PeerServices>())
.prop_map(|(socket_addr, untrusted_services)| {
MetaAddr::new_alternate(&socket_addr, &untrusted_services)
MetaAddr::new_alternate(socket_addr, &untrusted_services)
.into_new_meta_addr()
.expect("unexpected invalid alternate change")
})
@ -62,11 +60,10 @@ impl MetaAddr {
}
impl MetaAddrChange {
/// Returns a strategy which generates changes for `socket_addr`.
/// Returns a strategy which generates changes for `addr`.
///
/// `socket_addr` is typically generated by the `canonical_socket_addr`
/// strategy.
pub fn addr_strategy(addr: SocketAddr) -> BoxedStrategy<Self> {
/// `addr` is typically generated by the `canonical_peer_addr` strategy.
pub fn addr_strategy(addr: PeerSocketAddr) -> BoxedStrategy<Self> {
any::<MetaAddrChange>()
.prop_map(move |mut change| {
change.set_addr(addr);
@ -78,7 +75,7 @@ impl MetaAddrChange {
/// Returns a strategy which generates a `MetaAddr`, and a vector of up to
/// `max_addr_change` changes.
///
/// The address and the changes all have matching `SocketAddr`s.
/// The address and the changes all have matching `PeerSocketAddr`s.
pub fn addr_changes_strategy(
max_addr_change: usize,
) -> BoxedStrategy<(MetaAddr, Vec<MetaAddrChange>)> {
@ -101,12 +98,12 @@ impl MetaAddrChange {
///
/// [1]: super::NewAlternate
pub fn ready_outbound_strategy() -> BoxedStrategy<Self> {
canonical_socket_addr_strategy()
canonical_peer_addr_strategy()
.prop_filter_map("failed MetaAddr::is_valid_for_outbound", |addr| {
// Alternate nodes use the current time, so they're always ready
//
// TODO: create a "Zebra supported services" constant
let change = MetaAddr::new_alternate(&addr, &PeerServices::NODE_NETWORK);
let change = MetaAddr::new_alternate(addr, &PeerServices::NODE_NETWORK);
if change
.into_new_meta_addr()
.expect("unexpected invalid alternate change")

View File

@ -0,0 +1,66 @@
//! Wrappers for peer addresses which hide sensitive user and node operator details in logs and
//! metrics.
use std::{
fmt,
net::SocketAddr,
ops::{Deref, DerefMut},
str::FromStr,
};
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
/// A thin wrapper for [`SocketAddr`] which hides peer IP addresses in logs and metrics.
#[derive(
Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct PeerSocketAddr(SocketAddr);
impl fmt::Debug for PeerSocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for PeerSocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ip_version = if self.is_ipv4() { "v4" } else { "v6" };
// The port is usually not sensitive, and it's useful for debugging.
f.pad(&format!("{}redacted:{}", ip_version, self.port()))
}
}
impl FromStr for PeerSocketAddr {
type Err = <SocketAddr as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.parse()?))
}
}
impl<S> From<S> for PeerSocketAddr
where
S: Into<SocketAddr>,
{
fn from(value: S) -> Self {
Self(value.into())
}
}
impl Deref for PeerSocketAddr {
type Target = SocketAddr;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for PeerSocketAddr {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@ -75,7 +75,7 @@ pub(crate) fn sanitize_avoids_leaks(original: &MetaAddr, sanitized: &MetaAddr) {
assert_eq!(sanitized.services, Some(sanitized_peer_services));
// Remove IPv6 scope ID and flow information
let sanitized_socket_addr = SocketAddr::new(original.addr.ip(), original.addr.port());
let sanitized_socket_addr = SocketAddr::new(original.addr.ip(), original.addr.port()).into();
assert_eq!(sanitized.addr.ip(), original.addr.ip());
assert_eq!(sanitized.addr.port(), original.addr.port());
assert_eq!(sanitized.addr, sanitized_socket_addr);

View File

@ -1,9 +1,6 @@
//! Randomised property tests for MetaAddr and MetaAddrChange.
use std::{
collections::HashMap, convert::TryFrom, env, net::SocketAddr, str::FromStr, sync::Arc,
time::Duration,
};
use std::{collections::HashMap, env, net::SocketAddr, str::FromStr, sync::Arc, time::Duration};
use chrono::Utc;
use proptest::{collection::vec, prelude::*};
@ -21,8 +18,8 @@ use crate::{
PeerAddrState::*,
},
peer_set::candidate_set::CandidateSet,
protocol::{external::canonical_socket_addr, types::PeerServices},
AddressBook,
protocol::{external::canonical_peer_addr, types::PeerServices},
AddressBook, PeerSocketAddr,
};
use super::check;
@ -120,7 +117,7 @@ proptest! {
prop_assert!(attempt_count <= 1);
// Simulate an attempt
addr = MetaAddr::new_reconnect(&addr.addr)
addr = MetaAddr::new_reconnect(addr.addr)
.apply_to_meta_addr(addr)
.expect("unexpected invalid attempt");
}
@ -159,7 +156,7 @@ proptest! {
let sanitized_addrs = address_book.sanitized(chrono_now);
let expected_local_listener = address_book.local_listener_meta_addr();
let canonical_local_listener = canonical_socket_addr(local_listener);
let canonical_local_listener = canonical_peer_addr(local_listener);
let book_sanitized_local_listener = sanitized_addrs
.iter()
.find(|meta_addr| meta_addr.addr == canonical_local_listener);
@ -415,10 +412,10 @@ proptest! {
tokio::time::pause();
// The current attempt counts for each peer in this interval
let mut attempt_counts: HashMap<SocketAddr, u32> = HashMap::new();
let mut attempt_counts: HashMap<PeerSocketAddr, u32> = HashMap::new();
// The most recent address info for each peer
let mut addrs: HashMap<SocketAddr, MetaAddr> = HashMap::new();
let mut addrs: HashMap<PeerSocketAddr, MetaAddr> = HashMap::new();
for change_index in 0..MAX_ADDR_CHANGE {
for (addr, changes) in addr_changes_lists.iter() {
@ -432,7 +429,7 @@ proptest! {
);
// Simulate an attempt
*addr = MetaAddr::new_reconnect(&addr.addr)
*addr = MetaAddr::new_reconnect(addr.addr)
.apply_to_meta_addr(*addr)
.expect("unexpected invalid attempt");
}

View File

@ -1,7 +1,5 @@
//! Fixed test cases for MetaAddr and MetaAddrChange.
use std::net::SocketAddr;
use chrono::Utc;
use zebra_chain::{
@ -9,7 +7,7 @@ use zebra_chain::{
serialization::{DateTime32, Duration32},
};
use crate::{constants::MAX_PEER_ACTIVE_FOR_GOSSIP, protocol::types::PeerServices};
use crate::{constants::MAX_PEER_ACTIVE_FOR_GOSSIP, protocol::types::PeerServices, PeerSocketAddr};
use super::{super::MetaAddr, check};
@ -61,8 +59,8 @@ fn new_local_listener_is_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let peer = MetaAddr::new_local_listener_change(&address)
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer = MetaAddr::new_local_listener_change(address)
.into_new_meta_addr()
.expect("MetaAddrChange can't create a new MetaAddr");
@ -79,8 +77,8 @@ fn new_alternate_peer_address_is_not_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let peer = MetaAddr::new_alternate(&address, &PeerServices::NODE_NETWORK)
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer = MetaAddr::new_alternate(address, &PeerServices::NODE_NETWORK)
.into_new_meta_addr()
.expect("MetaAddrChange can't create a new MetaAddr");
@ -94,7 +92,7 @@ fn gossiped_peer_reportedly_to_be_seen_recently_is_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
// Report last seen within the reachable interval.
let offset = MAX_PEER_ACTIVE_FOR_GOSSIP
@ -116,7 +114,7 @@ fn gossiped_peer_reportedly_seen_in_the_future_is_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
// Report last seen in the future
let last_seen = DateTime32::now()
@ -135,7 +133,7 @@ fn gossiped_peer_reportedly_seen_long_ago_is_not_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
// Report last seen just outside the reachable interval.
let offset = MAX_PEER_ACTIVE_FOR_GOSSIP
@ -157,13 +155,13 @@ fn recently_responded_peer_is_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_alternate(&address, &PeerServices::NODE_NETWORK)
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_alternate(address, &PeerServices::NODE_NETWORK)
.into_new_meta_addr()
.expect("MetaAddrChange can't create a new MetaAddr");
// Create a peer that has responded
let peer = MetaAddr::new_responded(&address, &PeerServices::NODE_NETWORK)
let peer = MetaAddr::new_responded(address, &PeerServices::NODE_NETWORK)
.apply_to_meta_addr(peer_seed)
.expect("Failed to create MetaAddr for responded peer");
@ -177,13 +175,13 @@ fn not_so_recently_responded_peer_is_still_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_alternate(&address, &PeerServices::NODE_NETWORK)
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_alternate(address, &PeerServices::NODE_NETWORK)
.into_new_meta_addr()
.expect("MetaAddrChange can't create a new MetaAddr");
// Create a peer that has responded
let mut peer = MetaAddr::new_responded(&address, &PeerServices::NODE_NETWORK)
let mut peer = MetaAddr::new_responded(address, &PeerServices::NODE_NETWORK)
.apply_to_meta_addr(peer_seed)
.expect("Failed to create MetaAddr for responded peer");
@ -207,13 +205,13 @@ fn responded_long_ago_peer_is_not_gossipable() {
let chrono_now = Utc::now();
let address = SocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_alternate(&address, &PeerServices::NODE_NETWORK)
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_alternate(address, &PeerServices::NODE_NETWORK)
.into_new_meta_addr()
.expect("MetaAddrChange can't create a new MetaAddr");
// Create a peer that has responded
let mut peer = MetaAddr::new_responded(&address, &PeerServices::NODE_NETWORK)
let mut peer = MetaAddr::new_responded(address, &PeerServices::NODE_NETWORK)
.apply_to_meta_addr(peer_seed)
.expect("Failed to create MetaAddr for responded peer");

View File

@ -4,7 +4,6 @@ use std::{
collections::HashSet,
future::Future,
iter,
net::SocketAddr,
pin::Pin,
sync::Arc,
task::{Context, Poll},
@ -29,7 +28,7 @@ use crate::{
external::InventoryHash,
internal::{Request, Response},
},
BoxError,
BoxError, PeerSocketAddr,
};
#[cfg(any(test, feature = "proptest-impl"))]
@ -87,7 +86,7 @@ pub(crate) struct ClientRequest {
/// The peer address for registering missing inventory.
///
/// TODO: replace this with `ConnectedAddr`?
pub transient_addr: Option<SocketAddr>,
pub transient_addr: Option<PeerSocketAddr>,
/// The tracing context for the request, so that work the connection task does
/// processing messages in the context of this request will have correct context.
@ -166,7 +165,7 @@ pub(super) struct MissingInventoryCollector {
collector: broadcast::Sender<InventoryChange>,
/// The peer address for registering missing inventory.
transient_addr: SocketAddr,
transient_addr: PeerSocketAddr,
}
impl std::fmt::Debug for Client {
@ -263,7 +262,7 @@ impl MustUseClientResponseSender {
tx: oneshot::Sender<Result<Response, SharedPeerError>>,
request: &Request,
inv_collector: Option<broadcast::Sender<InventoryChange>>,
transient_addr: Option<SocketAddr>,
transient_addr: Option<PeerSocketAddr>,
) -> Self {
Self {
tx: Some(tx),
@ -341,7 +340,7 @@ impl MissingInventoryCollector {
pub fn new(
request: &Request,
inv_collector: Option<broadcast::Sender<InventoryChange>>,
transient_addr: Option<SocketAddr>,
transient_addr: Option<PeerSocketAddr>,
) -> Option<Box<MissingInventoryCollector>> {
if !request.is_inventory_download() {
return None;

View File

@ -2,7 +2,6 @@
use std::{
future::Future,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
};
@ -17,7 +16,7 @@ use zebra_chain::chain_tip::{ChainTip, NoChainTip};
use crate::{
peer::{Client, ConnectedAddr, Handshake, HandshakeRequest},
peer_set::ConnectionTracker,
BoxError, Request, Response,
BoxError, PeerSocketAddr, Request, Response,
};
/// A wrapper around [`Handshake`] that opens a TCP connection before
@ -60,7 +59,7 @@ where
/// Contains the information needed to make an outbound connection to the peer.
pub struct OutboundConnectorRequest {
/// The Zcash listener address of the peer.
pub addr: SocketAddr,
pub addr: PeerSocketAddr,
/// A connection tracker that reduces the open connection count when dropped.
///
@ -74,7 +73,7 @@ where
S::Future: Send,
C: ChainTip + Clone + Send + 'static,
{
type Response = (SocketAddr, Client);
type Response = (PeerSocketAddr, Client);
type Error = BoxError;
type Future =
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
@ -94,7 +93,7 @@ where
let connector_span = info_span!("connector", peer = ?connected_addr);
async move {
let tcp_stream = TcpStream::connect(addr).await?;
let tcp_stream = TcpStream::connect(*addr).await?;
let client = hs
.oneshot(HandshakeRequest::<TcpStream> {
data_stream: tcp_stream,

View File

@ -4,7 +4,7 @@ use std::{
cmp::min,
fmt,
future::Future,
net::{IpAddr, Ipv4Addr, SocketAddr},
net::{Ipv4Addr, SocketAddr},
panic,
pin::Pin,
sync::Arc,
@ -45,7 +45,7 @@ use crate::{
internal::{Request, Response},
},
types::MetaAddr,
BoxError, Config, VersionMessage,
BoxError, Config, PeerSocketAddr, VersionMessage,
};
#[cfg(test)]
@ -152,7 +152,7 @@ pub enum ConnectedAddr {
/// and port.
OutboundDirect {
/// The connected outbound remote address and port.
addr: SocketAddr,
addr: PeerSocketAddr,
},
/// The address we received from the OS, when a remote peer directly
@ -162,11 +162,10 @@ pub enum ConnectedAddr {
/// if its outbound address is the same as its listener address. But the port
/// is an ephemeral outbound TCP port, not a listener port.
InboundDirect {
/// The connected inbound remote address.
maybe_ip: IpAddr,
/// The connected inbound transient remote port.
transient_port: u16,
/// The connected inbound remote address and ephemeral port.
///
/// The IP address might be the address of a Zcash peer, but the port is an ephemeral port.
addr: PeerSocketAddr,
},
/// The proxy address we used to make an outbound connection.
@ -207,16 +206,13 @@ use ConnectedAddr::*;
impl ConnectedAddr {
/// Returns a new outbound directly connected addr.
pub fn new_outbound_direct(addr: SocketAddr) -> ConnectedAddr {
pub fn new_outbound_direct(addr: PeerSocketAddr) -> ConnectedAddr {
OutboundDirect { addr }
}
/// Returns a new inbound directly connected addr.
pub fn new_inbound_direct(addr: SocketAddr) -> ConnectedAddr {
InboundDirect {
maybe_ip: addr.ip(),
transient_port: addr.port(),
}
pub fn new_inbound_direct(addr: PeerSocketAddr) -> ConnectedAddr {
InboundDirect { addr }
}
/// Returns a new outbound connected addr via `proxy`.
@ -246,7 +242,7 @@ impl ConnectedAddr {
Isolated
}
/// Returns a `SocketAddr` that can be used to track this connection in the
/// Returns a `PeerSocketAddr` that can be used to track this connection in the
/// `AddressBook`.
///
/// `None` for inbound connections, proxy connections, and isolated
@ -264,7 +260,7 @@ impl ConnectedAddr {
/// `AddressBook` state.
///
/// TODO: remove the `get_` from these methods (Rust style avoids `get` prefixes)
pub fn get_address_book_addr(&self) -> Option<SocketAddr> {
pub fn get_address_book_addr(&self) -> Option<PeerSocketAddr> {
match self {
OutboundDirect { addr } => Some(*addr),
// TODO: consider using the canonical address of the peer to track
@ -273,7 +269,7 @@ impl ConnectedAddr {
}
}
/// Returns a `SocketAddr` that can be used to temporarily identify a
/// Returns a `PeerSocketAddr` that can be used to temporarily identify a
/// connection.
///
/// Isolated connections must not change Zebra's peer set or address book
@ -290,18 +286,15 @@ impl ConnectedAddr {
/// This address must not depend on the canonical address from the `Version`
/// message. Otherwise, malicious peers could interfere with other peers'
/// `PeerSet` state.
pub fn get_transient_addr(&self) -> Option<SocketAddr> {
pub fn get_transient_addr(&self) -> Option<PeerSocketAddr> {
match self {
OutboundDirect { addr } => Some(*addr),
InboundDirect {
maybe_ip,
transient_port,
} => Some(SocketAddr::new(*maybe_ip, *transient_port)),
InboundDirect { addr } => Some(*addr),
OutboundProxy {
transient_local_addr,
..
} => Some(*transient_local_addr),
InboundProxy { transient_addr } => Some(*transient_addr),
} => Some(PeerSocketAddr::from(*transient_local_addr)),
InboundProxy { transient_addr } => Some(PeerSocketAddr::from(*transient_addr)),
Isolated => None,
}
}
@ -332,8 +325,8 @@ impl ConnectedAddr {
/// remote address that we're currently connected to.
pub fn get_alternate_addrs(
&self,
mut canonical_remote: SocketAddr,
) -> impl Iterator<Item = SocketAddr> {
mut canonical_remote: PeerSocketAddr,
) -> impl Iterator<Item = PeerSocketAddr> {
let addrs = match self {
OutboundDirect { addr } => {
// Fixup unspecified addresses and ports using known good data
@ -355,9 +348,9 @@ impl ConnectedAddr {
}
}
InboundDirect { maybe_ip, .. } => {
InboundDirect { addr } => {
// Use the IP from the TCP connection, and the port the peer told us
let maybe_addr = SocketAddr::new(*maybe_ip, canonical_remote.port());
let maybe_addr = SocketAddr::new(addr.ip(), canonical_remote.port()).into();
// Try both addresses, but remove one duplicate if they match
if canonical_remote != maybe_addr {
@ -654,7 +647,7 @@ where
// an unspecified address for Isolated connections
Isolated => {
let unspec_ipv4 = get_unspecified_ipv4_addr(config.network);
(unspec_ipv4, PeerServices::empty(), unspec_ipv4)
(unspec_ipv4.into(), PeerServices::empty(), unspec_ipv4)
}
_ => {
let their_addr = connected_addr
@ -923,7 +916,7 @@ where
// `Version` messages.
let alternate_addrs = connected_addr.get_alternate_addrs(remote_canonical_addr);
for alt_addr in alternate_addrs {
let alt_addr = MetaAddr::new_alternate(&alt_addr, &remote_services);
let alt_addr = MetaAddr::new_alternate(alt_addr, &remote_services);
// awaiting a local task won't hang
let _ = address_book_updater.send(alt_addr).await;
}
@ -933,7 +926,7 @@ where
// the collector doesn't depend on network activity,
// so this await should not hang
let _ = address_book_updater
.send(MetaAddr::new_responded(&book_addr, &remote_services))
.send(MetaAddr::new_responded(book_addr, &remote_services))
.await;
}
@ -1023,7 +1016,7 @@ where
// so this await should not hang
let _ = inbound_ts_collector
.send(MetaAddr::new_responded(
&book_addr,
book_addr,
&remote_services,
))
.await;
@ -1040,7 +1033,7 @@ where
if let Some(book_addr) = connected_addr.get_address_book_addr() {
let _ = inbound_ts_collector
.send(MetaAddr::new_errored(&book_addr, remote_services))
.send(MetaAddr::new_errored(book_addr, remote_services))
.await;
}
}
@ -1408,7 +1401,7 @@ where
if let Some(book_addr) = connected_addr.get_address_book_addr() {
let _ = address_book_updater
.send(MetaAddr::new_errored(&book_addr, *remote_services))
.send(MetaAddr::new_errored(book_addr, *remote_services))
.await;
}
Err(err)
@ -1427,7 +1420,7 @@ async fn handle_heartbeat_shutdown(
if let Some(book_addr) = connected_addr.get_address_book_addr() {
let _ = address_book_updater
.send(MetaAddr::new_shutdown(&book_addr, *remote_services))
.send(MetaAddr::new_shutdown(book_addr, *remote_services))
.await;
}

View File

@ -4,6 +4,8 @@ use std::net::SocketAddr;
use zebra_chain::parameters::Network;
use crate::PeerSocketAddr;
use AttributePreference::*;
/// A level of preference for a peer attribute.
@ -67,13 +69,15 @@ impl PeerPreference {
///
/// Use the [`PeerPreference`] [`Ord`] implementation to sort preferred peers first.
pub fn new(
peer_addr: &SocketAddr,
peer_addr: impl Into<PeerSocketAddr>,
network: impl Into<Option<Network>>,
) -> Result<PeerPreference, &'static str> {
let peer_addr = peer_addr.into();
address_is_valid_for_outbound_connections(peer_addr, network)?;
// This check only prefers the configured network,
// because the address book and initial peer connections reject the port used by the other network.
// This check only prefers the configured network, because
// address_is_valid_for_outbound_connections() rejects the port used by the other network.
let canonical_port =
AttributePreference::preferred_from([8232, 18232].contains(&peer_addr.port()));
@ -81,7 +85,7 @@ impl PeerPreference {
}
}
/// Is the [`SocketAddr`] we have for this peer valid for outbound
/// Is the [`PeerSocketAddr`] we have for this peer valid for outbound
/// connections?
///
/// Since the addresses in the address book are unique, this check can be
@ -89,7 +93,7 @@ impl PeerPreference {
///
/// [`MetaAddr`]: crate::meta_addr::MetaAddr
pub fn address_is_valid_for_outbound_connections(
peer_addr: &SocketAddr,
peer_addr: PeerSocketAddr,
network: impl Into<Option<Network>>,
) -> Result<(), &'static str> {
// TODO: make private IP addresses an error unless a debug config is set (#3117)
@ -105,14 +109,14 @@ pub fn address_is_valid_for_outbound_connections(
);
}
address_is_valid_for_inbound_listeners(peer_addr, network)
address_is_valid_for_inbound_listeners(*peer_addr, network)
}
/// Is the supplied [`SocketAddr`] valid for inbound listeners on `network`?
///
/// This is used to check Zebra's configured Zcash listener port.
pub fn address_is_valid_for_inbound_listeners(
listener_addr: &SocketAddr,
listener_addr: SocketAddr,
network: impl Into<Option<Network>>,
) -> Result<(), &'static str> {
// TODO: make private IP addresses an error unless a debug config is set (#3117)

View File

@ -388,7 +388,7 @@ where
//
// We could send a reconnect change to the AddressBookUpdater when the peer is actually used,
// but channel order is not guaranteed, so we could accidentally re-use the same peer.
let next_peer = MetaAddr::new_reconnect(&next_peer.addr);
let next_peer = MetaAddr::new_reconnect(next_peer.addr);
guard.update(next_peer)
};
@ -407,7 +407,7 @@ where
/// Mark `addr` as a failed peer.
pub async fn report_failed(&mut self, addr: &MetaAddr) {
let addr = MetaAddr::new_errored(&addr.addr, addr.services);
let addr = MetaAddr::new_errored(addr.addr, addr.services);
// # Correctness
//

View File

@ -1,7 +1,6 @@
//! Fixed test vectors for CandidateSet.
use std::{
convert::TryInto,
net::{IpAddr, SocketAddr},
str::FromStr,
sync::Arc,
@ -245,7 +244,7 @@ fn mock_gossiped_peers(last_seen_times: impl IntoIterator<Item = DateTime<Utc>>)
.expect("`last_seen` time doesn't fit in a `DateTime32`");
MetaAddr::new_gossiped_meta_addr(
SocketAddr::new(IpAddr::from([192, 168, 1, index as u8]), 20_000),
SocketAddr::new(IpAddr::from([192, 168, 1, index as u8]), 20_000).into(),
PeerServices::NODE_NETWORK,
last_seen,
)

View File

@ -39,7 +39,7 @@ use crate::{
OutboundConnectorRequest, PeerPreference,
},
peer_set::{set::MorePeers, ActiveConnectionCounter, CandidateSet, ConnectionTracker, PeerSet},
AddressBook, BoxError, Config, Request, Response,
AddressBook, BoxError, Config, PeerSocketAddr, Request, Response,
};
#[cfg(test)]
@ -49,7 +49,7 @@ mod tests;
/// handshake.
///
/// This result comes from the `Handshaker`.
type DiscoveredPeer = Result<(SocketAddr, peer::Client), BoxError>;
type DiscoveredPeer = Result<(PeerSocketAddr, peer::Client), BoxError>;
/// Initialize a peer set, using a network `config`, `inbound_service`,
/// and `latest_chain_tip`.
@ -254,8 +254,11 @@ async fn add_initial_peers<S>(
address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
) -> Result<ActiveConnectionCounter, BoxError>
where
S: Service<OutboundConnectorRequest, Response = (SocketAddr, peer::Client), Error = BoxError>
+ Clone
S: Service<
OutboundConnectorRequest,
Response = (PeerSocketAddr, peer::Client),
Error = BoxError,
> + Clone
+ Send
+ 'static,
S::Future: Send + 'static,
@ -398,9 +401,9 @@ where
async fn limit_initial_peers(
config: &Config,
address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
) -> HashSet<SocketAddr> {
let all_peers: HashSet<SocketAddr> = config.initial_peers().await;
let mut preferred_peers: BTreeMap<PeerPreference, Vec<SocketAddr>> = BTreeMap::new();
) -> HashSet<PeerSocketAddr> {
let all_peers: HashSet<PeerSocketAddr> = config.initial_peers().await;
let mut preferred_peers: BTreeMap<PeerPreference, Vec<PeerSocketAddr>> = BTreeMap::new();
let all_peers_count = all_peers.len();
if all_peers_count > config.peerset_initial_target_size {
@ -413,7 +416,7 @@ async fn limit_initial_peers(
// Filter out invalid initial peers, and prioritise valid peers for initial connections.
// (This treats initial peers the same way we treat gossiped peers.)
for peer_addr in all_peers {
let preference = PeerPreference::new(&peer_addr, config.network);
let preference = PeerPreference::new(peer_addr, config.network);
match preference {
Ok(preference) => preferred_peers
@ -439,7 +442,7 @@ async fn limit_initial_peers(
// Split out the `initial_peers` that will be shuffled and returned,
// choosing preferred peers first.
let mut initial_peers: HashSet<SocketAddr> = HashSet::new();
let mut initial_peers: HashSet<PeerSocketAddr> = HashSet::new();
for better_peers in preferred_peers.values() {
let mut better_peers = better_peers.clone();
let (chosen_peers, _unused_peers) = better_peers.partial_shuffle(
@ -470,7 +473,7 @@ async fn limit_initial_peers(
pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
// Warn if we're configured using the wrong network port.
if let Err(wrong_addr) =
address_is_valid_for_inbound_listeners(&config.listen_addr, config.network)
address_is_valid_for_inbound_listeners(config.listen_addr, config.network)
{
warn!(
"We are configured with address {} on {:?}, but it could cause network issues. \
@ -551,6 +554,8 @@ where
};
if let Ok((tcp_stream, addr)) = inbound_result {
let addr: PeerSocketAddr = addr.into();
if active_inbound_connections.update_count()
>= config.peerset_inbound_connection_limit()
{
@ -644,7 +649,7 @@ enum CrawlerAction {
TimerCrawl { tick: Instant },
/// Handle a successfully connected handshake `peer_set_change`.
HandshakeConnected {
address: SocketAddr,
address: PeerSocketAddr,
client: peer::Client,
},
/// Handle a handshake failure to `failed_addr`.
@ -692,8 +697,11 @@ async fn crawl_and_dial<C, S>(
mut active_outbound_connections: ActiveConnectionCounter,
) -> Result<(), BoxError>
where
C: Service<OutboundConnectorRequest, Response = (SocketAddr, peer::Client), Error = BoxError>
+ Clone
C: Service<
OutboundConnectorRequest,
Response = (PeerSocketAddr, peer::Client),
Error = BoxError,
> + Clone
+ Send
+ 'static,
C::Future: Send + 'static,
@ -865,8 +873,11 @@ async fn dial<C>(
outbound_connection_tracker: ConnectionTracker,
) -> CrawlerAction
where
C: Service<OutboundConnectorRequest, Response = (SocketAddr, peer::Client), Error = BoxError>
+ Clone
C: Service<
OutboundConnectorRequest,
Response = (PeerSocketAddr, peer::Client),
Error = BoxError,
> + Clone
+ Send
+ 'static,
C::Future: Send + 'static,
@ -898,8 +909,8 @@ where
.await
}
impl From<Result<(SocketAddr, peer::Client), (MetaAddr, BoxError)>> for CrawlerAction {
fn from(dial_result: Result<(SocketAddr, peer::Client), (MetaAddr, BoxError)>) -> Self {
impl From<Result<(PeerSocketAddr, peer::Client), (MetaAddr, BoxError)>> for CrawlerAction {
fn from(dial_result: Result<(PeerSocketAddr, peer::Client), (MetaAddr, BoxError)>) -> Self {
use CrawlerAction::*;
match dial_result {
Ok((address, client)) => HandshakeConnected { address, client },

View File

@ -43,7 +43,7 @@ use crate::{
ActiveConnectionCounter, CandidateSet,
},
protocol::types::PeerServices,
AddressBook, BoxError, Config, Request, Response,
AddressBook, BoxError, Config, PeerSocketAddr, Request, Response,
};
use Network::*;
@ -1192,7 +1192,7 @@ async fn self_connections_should_fail() {
.expect("unexpected panic in address book");
let self_connection_status = unlocked_address_book
.get(&real_self_listener.addr())
.get(real_self_listener.addr())
.expect("unexpected dropped listener address in address book");
std::mem::drop(unlocked_address_book);
@ -1283,7 +1283,7 @@ async fn remnant_nonces_from_outbound_connections_are_limited() {
let connection_tracker = active_outbound_connections.track_connection();
let req = OutboundConnectorRequest {
addr,
addr: addr.into(),
connection_tracker,
};
@ -1462,8 +1462,11 @@ async fn spawn_crawler_with_peer_limit<C>(
outbound_connector: C,
) -> (Config, mpsc::Receiver<DiscoveredPeer>)
where
C: Service<OutboundConnectorRequest, Response = (SocketAddr, peer::Client), Error = BoxError>
+ Clone
C: Service<
OutboundConnectorRequest,
Response = (PeerSocketAddr, peer::Client),
Error = BoxError,
> + Clone
+ Send
+ 'static,
C::Future: Send + 'static,
@ -1482,8 +1485,11 @@ where
let mut fake_peer = None;
for address_number in 0..over_limit_peers {
let addr = SocketAddr::new(Ipv4Addr::new(127, 1, 1, address_number as _).into(), 1);
let addr =
MetaAddr::new_gossiped_meta_addr(addr, PeerServices::NODE_NETWORK, DateTime32::now());
let addr = MetaAddr::new_gossiped_meta_addr(
addr.into(),
PeerServices::NODE_NETWORK,
DateTime32::now(),
);
fake_peer = Some(addr);
let addr = addr
.new_gossiped_change()
@ -1674,8 +1680,11 @@ async fn spawn_add_initial_peers<C>(
JoinHandle<Result<(), BoxError>>,
)
where
C: Service<OutboundConnectorRequest, Response = (SocketAddr, peer::Client), Error = BoxError>
+ Clone
C: Service<
OutboundConnectorRequest,
Response = (PeerSocketAddr, peer::Client),
Error = BoxError,
> + Clone
+ Send
+ 'static,
C::Future: Send + 'static,

View File

@ -3,7 +3,6 @@
//! [RFC]: https://zebra.zfnd.org/dev/rfcs/0003-inventory-tracking.html
use std::{
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
};
@ -21,7 +20,7 @@ use zebra_chain::serialization::AtLeastOne;
use crate::{
constants::INVENTORY_ROTATION_INTERVAL,
protocol::{external::InventoryHash, internal::InventoryResponse},
BoxError,
BoxError, PeerSocketAddr,
};
use self::update::Update;
@ -79,7 +78,7 @@ pub type InventoryStatus<T> = InventoryResponse<T, T>;
///
/// For security reasons, all `notfound` rejections should be tracked.
/// This also helps with performance, if the hash is rare on the network.
pub type InventoryChange = InventoryStatus<(AtLeastOne<InventoryHash>, SocketAddr)>;
pub type InventoryChange = InventoryStatus<(AtLeastOne<InventoryHash>, PeerSocketAddr)>;
/// An internal marker used in inventory status hash maps.
type InventoryMarker = InventoryStatus<()>;
@ -94,10 +93,10 @@ pub struct InventoryRegistry {
/// period.
//
// TODO: split maps into available and missing, so we can limit them separately.
current: IndexMap<InventoryHash, IndexMap<SocketAddr, InventoryMarker>>,
current: IndexMap<InventoryHash, IndexMap<PeerSocketAddr, InventoryMarker>>,
/// Map tracking inventory statuses from the previous interval period.
prev: IndexMap<InventoryHash, IndexMap<SocketAddr, InventoryMarker>>,
prev: IndexMap<InventoryHash, IndexMap<PeerSocketAddr, InventoryMarker>>,
/// Stream of incoming inventory statuses to register.
inv_stream: Pin<
@ -119,20 +118,20 @@ impl std::fmt::Debug for InventoryRegistry {
impl InventoryChange {
/// Returns a new available inventory change from a single hash.
pub fn new_available(hash: InventoryHash, peer: SocketAddr) -> Self {
pub fn new_available(hash: InventoryHash, peer: PeerSocketAddr) -> Self {
InventoryStatus::Available((AtLeastOne::from_one(hash), peer))
}
/// Returns a new missing inventory change from a single hash.
#[allow(dead_code)]
pub fn new_missing(hash: InventoryHash, peer: SocketAddr) -> Self {
pub fn new_missing(hash: InventoryHash, peer: PeerSocketAddr) -> Self {
InventoryStatus::Missing((AtLeastOne::from_one(hash), peer))
}
/// Returns a new available multiple inventory change, if `hashes` contains at least one change.
pub fn new_available_multi<'a>(
hashes: impl IntoIterator<Item = &'a InventoryHash>,
peer: SocketAddr,
peer: PeerSocketAddr,
) -> Option<Self> {
let mut hashes: Vec<InventoryHash> = hashes.into_iter().copied().collect();
@ -153,7 +152,7 @@ impl InventoryChange {
/// Returns a new missing multiple inventory change, if `hashes` contains at least one change.
pub fn new_missing_multi<'a>(
hashes: impl IntoIterator<Item = &'a InventoryHash>,
peer: SocketAddr,
peer: PeerSocketAddr,
) -> Option<Self> {
let mut hashes: Vec<InventoryHash> = hashes.into_iter().copied().collect();
@ -220,14 +219,14 @@ impl InventoryRegistry {
}
/// Returns an iterator over addrs of peers that have recently advertised `hash` in their inventory.
pub fn advertising_peers(&self, hash: InventoryHash) -> impl Iterator<Item = &SocketAddr> {
pub fn advertising_peers(&self, hash: InventoryHash) -> impl Iterator<Item = &PeerSocketAddr> {
self.status_peers(hash)
.filter_map(|addr_status| addr_status.available())
}
/// Returns an iterator over addrs of peers that have recently missed `hash` in their inventory.
#[allow(dead_code)]
pub fn missing_peers(&self, hash: InventoryHash) -> impl Iterator<Item = &SocketAddr> {
pub fn missing_peers(&self, hash: InventoryHash) -> impl Iterator<Item = &PeerSocketAddr> {
self.status_peers(hash)
.filter_map(|addr_status| addr_status.missing())
}
@ -238,7 +237,7 @@ impl InventoryRegistry {
pub fn status_peers(
&self,
hash: InventoryHash,
) -> impl Iterator<Item = InventoryStatus<&SocketAddr>> {
) -> impl Iterator<Item = InventoryStatus<&PeerSocketAddr>> {
let prev = self.prev.get(&hash);
let current = self.current.get(&hash);
@ -258,7 +257,7 @@ impl InventoryRegistry {
}
/// Returns true if there is a current status entry for `hash` and `addr`.
pub fn has_current_status(&self, hash: InventoryHash, addr: SocketAddr) -> bool {
pub fn has_current_status(&self, hash: InventoryHash, addr: PeerSocketAddr) -> bool {
self.current
.get(&hash)
.and_then(|current| current.get(&addr))
@ -272,7 +271,7 @@ impl InventoryRegistry {
#[allow(dead_code)]
pub fn status_hashes(
&self,
) -> impl Iterator<Item = (&InventoryHash, &IndexMap<SocketAddr, InventoryMarker>)> {
) -> impl Iterator<Item = (&InventoryHash, &IndexMap<PeerSocketAddr, InventoryMarker>)> {
self.current.iter().chain(self.prev.iter())
}

View File

@ -1,6 +1,6 @@
//! Randomised property tests for the inventory registry.
use std::{collections::HashSet, net::SocketAddr};
use std::collections::HashSet;
use proptest::prelude::*;
@ -11,6 +11,7 @@ use crate::{
InventoryMarker,
},
protocol::external::{InventoryHash, Message},
PeerSocketAddr,
};
use InventoryHash::*;
@ -59,7 +60,7 @@ async fn inv_registry_inbound_wrapper_with(
status: InventoryMarker,
test_hashes: HashSet<InventoryHash>,
) {
let test_peer: SocketAddr = "1.1.1.1:1"
let test_peer: PeerSocketAddr = "1.1.1.1:1"
.parse()
.expect("unexpected invalid peer address");
let test_peer = ConnectedAddr::new_inbound_direct(test_peer);

View File

@ -10,6 +10,7 @@ use crate::{
MAX_PEERS_PER_INV,
},
protocol::external::InventoryHash,
PeerSocketAddr,
};
/// Check an empty inventory registry works as expected.
@ -242,10 +243,11 @@ async fn inv_registry_limit_for(status: InventoryMarker) {
let (mut inv_registry, inv_stream_tx) = new_inv_registry();
for peer_count in 0..(MAX_PEERS_PER_INV + 10) {
let test_peer = SocketAddr::new(
let test_peer: PeerSocketAddr = SocketAddr::new(
"2.2.2.2".parse().unwrap(),
peer_count.try_into().expect("fits in u16"),
);
)
.into();
let test_change = status.map(|()| (AtLeastOne::from_one(single_test_hash), test_peer));

View File

@ -98,7 +98,6 @@ use std::{
fmt::Debug,
future::Future,
marker::PhantomData,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
time::Instant,
@ -133,7 +132,7 @@ use crate::{
external::InventoryHash,
internal::{Request, Response},
},
BoxError, Config, PeerError, SharedPeerError,
BoxError, Config, PeerError, PeerSocketAddr, SharedPeerError,
};
#[cfg(test)]
@ -166,7 +165,7 @@ pub struct CancelClientWork;
/// Otherwise, malicious peers could interfere with other peers' `PeerSet` state.
pub struct PeerSet<D, C>
where
D: Discover<Key = SocketAddr, Service = LoadTrackedClient> + Unpin,
D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
D::Error: Into<BoxError>,
C: ChainTip,
{
@ -252,7 +251,7 @@ where
impl<D, C> Drop for PeerSet<D, C>
where
D: Discover<Key = SocketAddr, Service = LoadTrackedClient> + Unpin,
D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
D::Error: Into<BoxError>,
C: ChainTip,
{
@ -263,7 +262,7 @@ where
impl<D, C> PeerSet<D, C>
where
D: Discover<Key = SocketAddr, Service = LoadTrackedClient> + Unpin,
D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
D::Error: Into<BoxError>,
C: ChainTip,
{
@ -711,7 +710,7 @@ where
return fut.map_err(Into::into).boxed();
}
let missing_peer_list: HashSet<SocketAddr> = self
let missing_peer_list: HashSet<PeerSocketAddr> = self
.inventory_registry
.missing_peers(hash)
.copied()
@ -883,7 +882,7 @@ where
impl<D, C> Service<Request> for PeerSet<D, C>
where
D: Discover<Key = SocketAddr, Service = LoadTrackedClient> + Unpin,
D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
D::Error: Into<BoxError>,
C: ChainTip,
{

View File

@ -26,7 +26,7 @@ use crate::{
peer::{ClientTestHarness, LoadTrackedClient, MinimumPeerVersion},
peer_set::{set::MorePeers, InventoryChange, PeerSet},
protocol::external::types::Version,
AddressBook, Config,
AddressBook, Config, PeerSocketAddr,
};
#[cfg(test)]
@ -86,14 +86,14 @@ impl PeerVersions {
pub fn mock_peer_discovery(
&self,
) -> (
impl Stream<Item = Result<Change<SocketAddr, LoadTrackedClient>, BoxError>>,
impl Stream<Item = Result<Change<PeerSocketAddr, LoadTrackedClient>, BoxError>>,
Vec<ClientTestHarness>,
) {
let (clients, harnesses) = self.mock_peers();
let fake_ports = 1_u16..;
let discovered_peers_iterator = fake_ports.zip(clients).map(|(port, client)| {
let peer_address = SocketAddr::new([127, 0, 0, 1].into(), port);
let peer_address: PeerSocketAddr = SocketAddr::new([127, 0, 0, 1].into(), port).into();
Ok(Change::Insert(peer_address, client))
});
@ -159,7 +159,7 @@ impl<D, C> PeerSetBuilder<D, C> {
impl<D, C> PeerSetBuilder<D, C>
where
D: Discover<Key = SocketAddr, Service = LoadTrackedClient> + Unpin,
D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
D::Error: Into<BoxError>,
C: ChainTip,
{

View File

@ -10,15 +10,16 @@ use zebra_chain::{
block, chain_tip::ChainTip, parameters::Network, serialization::ZcashDeserializeInto,
};
use super::{BlockHeightPairAcrossNetworkUpgrades, PeerSetBuilder, PeerVersions};
use crate::{
constants::CURRENT_NETWORK_PROTOCOL_VERSION,
peer::{ClientTestHarness, LoadTrackedClient, MinimumPeerVersion, ReceiveRequestAttempt},
peer_set::PeerSet,
protocol::external::types::Version,
Request,
PeerSocketAddr, Request,
};
use super::{BlockHeightPairAcrossNetworkUpgrades, PeerSetBuilder, PeerVersions};
proptest! {
/// Check if discovered outdated peers are immediately dropped by the [`PeerSet`].
#[test]
@ -200,7 +201,7 @@ proptest! {
// Remove peers, test broadcast until there is only 1 peer left in the peerset
for port in 1u16..total_number_of_peers as u16 {
peer_set.remove(&SocketAddr::new([127, 0, 0, 1].into(), port));
peer_set.remove(&SocketAddr::new([127, 0, 0, 1].into(), port).into());
handles.remove(0);
// poll the peers
@ -270,7 +271,7 @@ proptest! {
// Remove peers
for port in 1u16..=total_number_of_peers as u16 {
peer_set.remove(&SocketAddr::new([127, 0, 0, 1].into(), port));
peer_set.remove(&SocketAddr::new([127, 0, 0, 1].into(), port).into());
handles.remove(0);
}
@ -294,7 +295,7 @@ fn check_if_only_up_to_date_peers_are_live<D, C>(
minimum_version: Version,
) -> Result<usize, TestCaseError>
where
D: Discover<Key = SocketAddr, Service = LoadTrackedClient> + Unpin,
D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
D::Error: Into<BoxError>,
C: ChainTip,
{

View File

@ -16,7 +16,7 @@ pub mod arbitrary;
#[cfg(test)]
mod tests;
pub use addr::{canonical_socket_addr, AddrInVersion};
pub use addr::{canonical_peer_addr, canonical_socket_addr, AddrInVersion};
pub use codec::Codec;
pub use inv::InventoryHash;
pub use message::{Message, VersionMessage};

View File

@ -11,7 +11,7 @@ pub mod in_version;
pub(crate) mod v1;
pub(crate) mod v2;
pub use canonical::canonical_socket_addr;
pub use canonical::{canonical_peer_addr, canonical_socket_addr};
pub use in_version::AddrInVersion;
// These types and functions should only be visible in the `external` module,
@ -22,7 +22,7 @@ pub(super) use v2::AddrV2;
#[allow(unused_imports)]
#[cfg(any(test, feature = "proptest-impl"))]
pub(super) use v1::{ipv6_mapped_socket_addr, ADDR_V1_SIZE};
pub(super) use v1::{ipv6_mapped_ip_addr, ADDR_V1_SIZE};
// TODO: write tests for addrv2 deserialization
#[allow(unused_imports)]

View File

@ -1,34 +1,37 @@
//! Zebra's canonical node address format.
//!
//! Zebra canonicalises all received addresses into Rust [`SocketAddr`]s.
//! Zebra canonicalises all received addresses into Rust [`PeerSocketAddr`]s.
//! If the address is an [IPv4-mapped IPv6 address], it becomes a [`SocketAddr::V4`]
//!
//! [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
/// Transform a Zcash-deserialized IPv6 address into a canonical Zebra IP address.
use crate::PeerSocketAddr;
/// Transform a Zcash-deserialized [`Ipv6Addr`] into a canonical Zebra [`IpAddr`].
///
/// Zcash uses IPv6-mapped IPv4 addresses in its `addr` (v1) network messages.
/// Zebra converts those addresses to `Ipv4Addr`s, for maximum compatibility
/// with systems that don't understand IPv6.
///
/// Zebra also uses this canonical format for addresses from other sources.
pub fn canonical_ip_addr(v6_addr: &Ipv6Addr) -> IpAddr {
pub(in super::super) fn canonical_ip_addr(v6_addr: &Ipv6Addr) -> IpAddr {
use IpAddr::*;
// TODO: replace with `to_ipv4_mapped` when that stabilizes
// https://github.com/rust-lang/rust/issues/27709
match v6_addr.to_ipv4() {
// workaround for unstable `to_ipv4_mapped`
Some(v4_addr) if v4_addr.to_ipv6_mapped() == *v6_addr => V4(v4_addr),
Some(_) | None => V6(*v6_addr),
}
// if it is an IPv4-mapped address, convert to V4, otherwise leave it as V6
v6_addr
.to_ipv4_mapped()
.map(V4)
.unwrap_or_else(|| V6(*v6_addr))
}
/// Transform a `SocketAddr` into a canonical Zebra `SocketAddr`, converting
/// Transform a [`SocketAddr`] into a canonical Zebra [`SocketAddr`], converting
/// IPv6-mapped IPv4 addresses, and removing IPv6 scope IDs and flow information.
///
/// Use [`canonical_peer_addr()`] and [`PeerSocketAddr`] for remote peer addresses,
/// so that Zebra doesn't log sensitive information about peers.
///
/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
pub fn canonical_socket_addr(socket_addr: impl Into<SocketAddr>) -> SocketAddr {
use SocketAddr::*;
@ -42,3 +45,13 @@ pub fn canonical_socket_addr(socket_addr: impl Into<SocketAddr>) -> SocketAddr {
socket_addr
}
/// Transform a [`PeerSocketAddr`] into a canonical Zebra [`PeerSocketAddr`], converting
/// IPv6-mapped IPv4 addresses, and removing IPv6 scope IDs and flow information.
///
/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
pub fn canonical_peer_addr(peer_socket_addr: impl Into<PeerSocketAddr>) -> PeerSocketAddr {
let peer_socket_addr = peer_socket_addr.into();
canonical_socket_addr(*peer_socket_addr).into()
}

View File

@ -5,7 +5,7 @@
use std::{
io::{Read, Write},
net::{SocketAddr, SocketAddrV6},
net::SocketAddrV6,
};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
@ -14,15 +14,15 @@ use zebra_chain::serialization::{
SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
};
use crate::protocol::external::types::PeerServices;
use crate::{protocol::external::types::PeerServices, PeerSocketAddr};
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
use crate::protocol::external::arbitrary::addr_v1_ipv6_mapped_socket_addr_strategy;
use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
use super::{canonical_socket_addr, v1::ipv6_mapped_socket_addr};
use super::{canonical_peer_addr, v1::ipv6_mapped_ip_addr};
/// The format used for Bitcoin node addresses in `version` messages.
/// Contains a node address and services, without a last-seen time.
@ -31,9 +31,9 @@ use super::{canonical_socket_addr, v1::ipv6_mapped_socket_addr};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct AddrInVersion {
/// The unverified services for the peer at `ipv6_addr`.
/// The unverified services for the peer at `addr`.
///
/// These services were advertised by the peer at `ipv6_addr`,
/// These services were advertised by the peer at `addr`,
/// then gossiped via another peer.
///
/// ## Security
@ -42,29 +42,29 @@ pub struct AddrInVersion {
/// records, older peer versions, or buggy or malicious peers.
untrusted_services: PeerServices,
/// The peer's IPv6 socket address.
/// The peer's canonical socket address.
/// IPv4 addresses are serialized as an [IPv4-mapped IPv6 address].
///
/// [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "addr_v1_ipv6_mapped_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
ipv6_addr: SocketAddrV6,
addr: PeerSocketAddr,
}
impl AddrInVersion {
/// Returns a new `version` message address based on its fields.
pub fn new(socket_addr: impl Into<SocketAddr>, untrusted_services: PeerServices) -> Self {
pub fn new(socket_addr: impl Into<PeerSocketAddr>, untrusted_services: PeerServices) -> Self {
Self {
untrusted_services,
ipv6_addr: ipv6_mapped_socket_addr(socket_addr),
addr: canonical_peer_addr(socket_addr),
}
}
/// Returns the canonical address for this peer.
pub fn addr(&self) -> SocketAddr {
canonical_socket_addr(self.ipv6_addr)
pub fn addr(&self) -> PeerSocketAddr {
self.addr
}
/// Returns the services for this peer.
@ -77,8 +77,9 @@ impl ZcashSerialize for AddrInVersion {
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
writer.write_u64::<LittleEndian>(self.untrusted_services.bits())?;
self.ipv6_addr.ip().zcash_serialize(&mut writer)?;
writer.write_u16::<BigEndian>(self.ipv6_addr.port())?;
let ipv6_addr = ipv6_mapped_ip_addr(self.addr.ip());
ipv6_addr.zcash_serialize(&mut writer)?;
writer.write_u16::<BigEndian>(self.addr.port())?;
Ok(())
}
@ -96,7 +97,7 @@ impl ZcashDeserialize for AddrInVersion {
let ipv6_addr = SocketAddrV6::new(ipv6_addr, port, 0, 0);
Ok(AddrInVersion {
ipv6_addr,
addr: canonical_peer_addr(ipv6_addr),
untrusted_services,
})
}

View File

@ -7,7 +7,7 @@
use std::{
io::{Read, Write},
net::{IpAddr, Ipv6Addr, SocketAddr, SocketAddrV6},
net::{IpAddr, Ipv6Addr, SocketAddrV6},
};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
@ -20,13 +20,16 @@ use zebra_chain::serialization::{
use crate::{
meta_addr::MetaAddr,
protocol::external::{types::PeerServices, MAX_PROTOCOL_MESSAGE_LEN},
PeerSocketAddr,
};
use super::canonical_peer_addr;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
use crate::protocol::external::arbitrary::addr_v1_ipv6_mapped_socket_addr_strategy;
use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
/// The first format used for Bitcoin node addresses.
/// Contains a node address, its advertised services, and last-seen time.
@ -42,9 +45,9 @@ pub(in super::super) struct AddrV1 {
/// See the [`MetaAddr::last_seen`] method for details.
untrusted_last_seen: DateTime32,
/// The unverified services for the peer at `ipv6_addr`.
/// The unverified services for the peer at `addr`.
///
/// These services were advertised by the peer at `ipv6_addr`,
/// These services were advertised by the peer at `addr`,
/// then gossiped via another peer.
///
/// ## Security
@ -53,20 +56,20 @@ pub(in super::super) struct AddrV1 {
/// records, older peer versions, or buggy or malicious peers.
untrusted_services: PeerServices,
/// The peer's IPv6 socket address.
/// The peer's canonical socket address.
/// IPv4 addresses are serialized as an [IPv4-mapped IPv6 address].
///
/// [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "addr_v1_ipv6_mapped_socket_addr_strategy()")
proptest(strategy = "canonical_peer_addr_strategy()")
)]
ipv6_addr: SocketAddrV6,
addr: PeerSocketAddr,
}
impl From<MetaAddr> for AddrV1 {
fn from(meta_addr: MetaAddr) -> Self {
let ipv6_addr = ipv6_mapped_socket_addr(meta_addr.addr);
let addr = canonical_peer_addr(meta_addr.addr);
let untrusted_services = meta_addr.services.expect(
"unexpected MetaAddr with missing peer services: \
@ -80,7 +83,7 @@ impl From<MetaAddr> for AddrV1 {
AddrV1 {
untrusted_last_seen,
untrusted_services,
ipv6_addr,
addr,
}
}
}
@ -88,7 +91,7 @@ impl From<MetaAddr> for AddrV1 {
impl From<AddrV1> for MetaAddr {
fn from(addr: AddrV1) -> Self {
MetaAddr::new_gossiped_meta_addr(
addr.ipv6_addr.into(),
addr.addr,
addr.untrusted_services,
addr.untrusted_last_seen,
)
@ -100,8 +103,9 @@ impl ZcashSerialize for AddrV1 {
self.untrusted_last_seen.zcash_serialize(&mut writer)?;
writer.write_u64::<LittleEndian>(self.untrusted_services.bits())?;
self.ipv6_addr.ip().zcash_serialize(&mut writer)?;
writer.write_u16::<BigEndian>(self.ipv6_addr.port())?;
let ipv6_addr = ipv6_mapped_ip_addr(self.addr.ip());
ipv6_addr.zcash_serialize(&mut writer)?;
writer.write_u16::<BigEndian>(self.addr.port())?;
Ok(())
}
@ -120,7 +124,7 @@ impl ZcashDeserialize for AddrV1 {
let ipv6_addr = SocketAddrV6::new(ipv6_addr, port, 0, 0);
Ok(AddrV1 {
ipv6_addr,
addr: canonical_peer_addr(ipv6_addr),
untrusted_services,
untrusted_last_seen,
})
@ -137,36 +141,16 @@ impl TrustedPreallocate for AddrV1 {
}
}
/// Transform a `SocketAddr` into an IPv6-mapped IPv4 addresses.
/// Transform an `IpAddr` into an IPv6-mapped IPv4 addresses.
///
/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
///
/// [`canonical_ip_addr`]: super::canonical::canonical_ip_addr
pub(in super::super) fn ipv6_mapped_ip_addr(ip_addr: &IpAddr) -> Ipv6Addr {
pub(in super::super) fn ipv6_mapped_ip_addr(ip_addr: IpAddr) -> Ipv6Addr {
use IpAddr::*;
match ip_addr {
V4(v4_addr) => v4_addr.to_ipv6_mapped(),
V6(v6_addr) => *v6_addr,
V6(v6_addr) => v6_addr,
}
}
/// Transform a `SocketAddr` into an IPv6-mapped IPv4 addresses,
/// for `addr` (v1) Zcash network messages.
///
/// Also remove IPv6 scope IDs and flow information.
///
/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
///
/// [`canonical_ip_addr`]: super::canonical::canonical_ip_addr
pub(in super::super) fn ipv6_mapped_socket_addr(
socket_addr: impl Into<SocketAddr>,
) -> SocketAddrV6 {
let socket_addr = socket_addr.into();
let ipv6_mapped_ip = ipv6_mapped_ip_addr(&socket_addr.ip());
// Remove scope IDs and flow information.
// `0` is the default unspecified value for these fields.
SocketAddrV6::new(ipv6_mapped_ip, socket_addr.port(), 0, 0)
}

View File

@ -21,8 +21,11 @@ use zebra_chain::serialization::{
use crate::{
meta_addr::MetaAddr,
protocol::external::{types::PeerServices, MAX_PROTOCOL_MESSAGE_LEN},
PeerSocketAddr,
};
use super::canonical_peer_addr;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
@ -100,18 +103,18 @@ pub(in super::super) enum AddrV2 {
/// records, older peer versions, or buggy or malicious peers.
untrusted_services: PeerServices,
/// The peer's IP address.
/// The peer's canonical IP address and port.
///
/// Unlike [`AddrV1`], this can be an IPv4 or IPv6 address.
///
/// [`AddrV1`]: super::v1::AddrV1
ip: IpAddr,
/// The peer's TCP port.
port: u16,
addr: PeerSocketAddr,
},
/// A node address with an unsupported `networkID`, in `addrv2` format.
//
// TODO: when we add more address types, make sure their addresses aren't logged,
// in a similar way to `PeerSocketAddr`
Unsupported,
}
@ -138,8 +141,7 @@ impl From<MetaAddr> for AddrV2 {
AddrV2::IpAddr {
untrusted_last_seen,
untrusted_services,
ip: meta_addr.addr.ip(),
port: meta_addr.addr.port(),
addr: canonical_peer_addr(meta_addr.addr()),
}
}
}
@ -157,12 +159,9 @@ impl TryFrom<AddrV2> for MetaAddr {
if let AddrV2::IpAddr {
untrusted_last_seen,
untrusted_services,
ip,
port,
addr,
} = addr
{
let addr = SocketAddr::new(ip, port);
Ok(MetaAddr::new_gossiped_meta_addr(
addr,
untrusted_services,
@ -219,8 +218,7 @@ impl ZcashSerialize for AddrV2 {
if let AddrV2::IpAddr {
untrusted_last_seen,
untrusted_services,
ip,
port,
addr,
} = self
{
// > uint32 Time that this node was last seen as connected to the network.
@ -230,7 +228,7 @@ impl ZcashSerialize for AddrV2 {
let untrusted_services: CompactSize64 = untrusted_services.bits().into();
untrusted_services.zcash_serialize(&mut writer)?;
match ip {
match addr.ip() {
IpAddr::V4(ip) => {
// > Network identifier. An 8-bit value that specifies which network is addressed.
writer.write_u8(ADDR_V2_IPV4_NETWORK_ID)?;
@ -243,7 +241,7 @@ impl ZcashSerialize for AddrV2 {
zcash_serialize_bytes(&ip.to_vec(), &mut writer)?;
// > uint16 Network port. If not relevant for the network this MUST be 0.
writer.write_u16::<BigEndian>(*port)?;
writer.write_u16::<BigEndian>(addr.port())?;
}
IpAddr::V6(ip) => {
writer.write_u8(ADDR_V2_IPV6_NETWORK_ID)?;
@ -251,7 +249,7 @@ impl ZcashSerialize for AddrV2 {
let ip: [u8; ADDR_V2_IPV6_ADDR_SIZE] = ip.octets();
zcash_serialize_bytes(&ip.to_vec(), &mut writer)?;
writer.write_u16::<BigEndian>(*port)?;
writer.write_u16::<BigEndian>(addr.port())?;
}
}
} else {
@ -313,8 +311,7 @@ impl ZcashDeserialize for AddrV2 {
Ok(AddrV2::IpAddr {
untrusted_last_seen,
untrusted_services,
ip,
port,
addr: canonical_peer_addr(SocketAddr::new(ip, port)),
})
}
}

View File

@ -1,14 +1,15 @@
use std::{
convert::TryInto,
net::{SocketAddr, SocketAddrV6},
};
//! Randomised test data generation for external protocol types.
use std::net::SocketAddr;
use proptest::{arbitrary::any, arbitrary::Arbitrary, collection::vec, prelude::*};
use zebra_chain::{block, transaction};
use crate::PeerSocketAddr;
use super::{
addr::{canonical_socket_addr, ipv6_mapped_socket_addr},
addr::canonical_peer_addr,
types::{PeerServices, Version},
InventoryHash, Message,
};
@ -124,21 +125,11 @@ impl Arbitrary for Version {
type Strategy = BoxedStrategy<Self>;
}
/// Returns a random canonical Zebra `SocketAddr`.
/// Returns a random canonical Zebra [`PeerSocketAddr`].
///
/// See [`canonical_ip_addr`] for details.
///
/// [`canonical_ip_addr`]: super::addr::canonical::canonical_ip_addr
pub fn canonical_socket_addr_strategy() -> impl Strategy<Value = SocketAddr> {
any::<SocketAddr>().prop_map(canonical_socket_addr)
}
/// Returns a random `SocketAddrV6` for use in `addr` (v1) Zcash network
/// messages.
///
/// See [`canonical_ip_addr`] for details.
///
/// [`canonical_ip_addr`]: super::addr::canonical::canonical_ip_addr
pub fn addr_v1_ipv6_mapped_socket_addr_strategy() -> impl Strategy<Value = SocketAddrV6> {
any::<SocketAddr>().prop_map(ipv6_mapped_socket_addr)
pub fn canonical_peer_addr_strategy() -> impl Strategy<Value = PeerSocketAddr> {
any::<SocketAddr>().prop_map(canonical_peer_addr)
}

View File

@ -32,7 +32,7 @@ pub enum Response {
///
/// The list contains `0..=MAX_META_ADDR` peers.
//
// TODO: make this into a HashMap<SocketAddr, MetaAddr> - a unique list of peer addresses (#2244)
// TODO: make this into a HashMap<PeerSocketAddr, MetaAddr> - a unique list of peer addresses (#2244)
Peers(Vec<MetaAddr>),
/// An ordered list of block hashes.

View File

@ -1,7 +1,7 @@
//! Acceptance tests for zebra-network APIs.
use std::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
net::{Ipv4Addr, SocketAddrV4},
sync::Arc,
};
@ -10,14 +10,14 @@ use chrono::Utc;
use zebra_chain::block::Height;
use zebra_network::{
types::{AddrInVersion, Nonce, PeerServices},
ConnectedAddr, ConnectionInfo, Version, VersionMessage,
ConnectedAddr, ConnectionInfo, PeerSocketAddr, Version, VersionMessage,
};
/// Test that the types used in [`ConnectionInfo`] are public,
/// by compiling code that explicitly uses those types.
#[test]
fn connection_info_types_are_public() {
let fake_addr: SocketAddr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 3).into();
let fake_addr: PeerSocketAddr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 3).into();
let fake_version = Version(3);
let fake_services = PeerServices::default();

View File

@ -1,14 +1,12 @@
//! An array of [`PeerInfo`] is the output of the `getpeerinfo` RPC method.
use std::net::SocketAddr;
use zebra_network::types::MetaAddr;
use zebra_network::{types::MetaAddr, PeerSocketAddr};
/// Item of the `getpeerinfo` response
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct PeerInfo {
/// The IP address and port of the peer
pub addr: SocketAddr,
pub addr: PeerSocketAddr,
}
impl From<MetaAddr> for PeerInfo {

View File

@ -125,13 +125,15 @@ pub async fn test_responses<State, ReadState>(
mock_chain_tip_sender.send_best_tip_hash(fake_tip_hash);
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(0));
let mock_address_book =
MockAddressBookPeers::new(vec![MetaAddr::new_initial_peer(SocketAddr::new(
let mock_address_book = MockAddressBookPeers::new(vec![MetaAddr::new_initial_peer(
SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
network.default_port(),
))
.into_new_meta_addr()
.unwrap()]);
)
.into(),
)
.into_new_meta_addr()
.unwrap()]);
// get an rpc instance with continuous blockchain state
let get_block_template_rpc = GetBlockTemplateRpcImpl::new(

View File

@ -944,13 +944,15 @@ async fn rpc_getpeerinfo() {
)
.await;
let mock_peer_address =
zebra_network::types::MetaAddr::new_initial_peer(std::net::SocketAddr::new(
let mock_peer_address = zebra_network::types::MetaAddr::new_initial_peer(
std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)),
network.default_port(),
))
.into_new_meta_addr()
.unwrap();
)
.into(),
)
.into_new_meta_addr()
.unwrap();
let mock_address_book = MockAddressBookPeers::new(vec![mock_peer_address]);

View File

@ -20,8 +20,8 @@ use zebra_chain::{
};
use zebra_consensus::{chain::VerifyChainError, error::TransactionError, transaction};
use zebra_network::{
connect_isolated_tcp_direct_with_inbound, types::InventoryHash, Config as NetworkConfig,
InventoryResponse, PeerError, Request, Response, SharedPeerError,
canonical_peer_addr, connect_isolated_tcp_direct_with_inbound, types::InventoryHash,
Config as NetworkConfig, InventoryResponse, PeerError, Request, Response, SharedPeerError,
};
use zebra_node_services::mempool;
use zebra_state::Config as StateConfig;
@ -67,7 +67,10 @@ async fn inbound_peers_empty_address_book() -> Result<(), crate::BoxError> {
let response = request.await;
match response.as_ref() {
Ok(Response::Peers(single_peer)) if single_peer.len() == 1 => {
assert_eq!(single_peer.first().unwrap().addr(), listen_addr)
assert_eq!(
single_peer.first().unwrap().addr(),
canonical_peer_addr(listen_addr)
)
}
Ok(Response::Peers(_peer_list)) => unreachable!(
"`Peers` response should contain a single peer, \
@ -86,7 +89,10 @@ async fn inbound_peers_empty_address_book() -> Result<(), crate::BoxError> {
let response = request.await;
match response.as_ref() {
Ok(Response::Peers(single_peer)) if single_peer.len() == 1 => {
assert_eq!(single_peer.first().unwrap().addr(), listen_addr)
assert_eq!(
single_peer.first().unwrap().addr(),
canonical_peer_addr(listen_addr)
)
}
Ok(Response::Peers(_peer_list)) => unreachable!(
"`Peers` response should contain a single peer, \
@ -653,11 +659,8 @@ async fn setup(
.await;
// Inbound listener
let listen_addr = address_book
.lock()
.unwrap()
.local_listener_meta_addr()
.addr();
let listen_addr = address_book.lock().unwrap().local_listener_socket_addr();
assert_ne!(
listen_addr.port(),
0,