Async DNS seeder lookups (#1662)
* replace to_socket_addrs * refactor `resolve()` into `resolve_host()` * use `resolve_host()` to resolve config peers * add DNS_LOOKUP_TIMEOUT constant * don't block the main thread in initialize
This commit is contained in:
parent
6679a124e3
commit
221512c733
|
|
@ -1,9 +1,4 @@
|
||||||
use std::{
|
use std::{collections::HashSet, net::SocketAddr, string::String, time::Duration};
|
||||||
collections::HashSet,
|
|
||||||
net::{SocketAddr, ToSocketAddrs},
|
|
||||||
string::String,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use zebra_chain::parameters::Network;
|
use zebra_chain::parameters::Network;
|
||||||
|
|
||||||
|
|
@ -37,19 +32,55 @@ pub struct Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
fn parse_peers<S: ToSocketAddrs>(peers: HashSet<S>) -> HashSet<SocketAddr> {
|
/// Concurrently resolves `peers` into zero or more IP addresses, with a timeout
|
||||||
peers
|
/// of a few seconds on each DNS request.
|
||||||
|
///
|
||||||
|
/// If DNS resolution fails or times out for all peers, returns an empty list.
|
||||||
|
async fn parse_peers(peers: &HashSet<String>) -> HashSet<SocketAddr> {
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
let peer_addresses = peers
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|s| s.to_socket_addrs())
|
.map(|s| Config::resolve_host(s))
|
||||||
.flatten()
|
.collect::<futures::stream::FuturesUnordered<_>>()
|
||||||
.collect()
|
.concat()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if peer_addresses.is_empty() {
|
||||||
|
tracing::warn!(
|
||||||
|
?peers,
|
||||||
|
?peer_addresses,
|
||||||
|
"empty peer list after DNS resolution"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
peer_addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the initial seed peers based on the configured network.
|
/// Get the initial seed peers based on the configured network.
|
||||||
pub fn initial_peers(&self) -> HashSet<SocketAddr> {
|
pub async fn initial_peers(&self) -> HashSet<SocketAddr> {
|
||||||
match self.network {
|
match self.network {
|
||||||
Network::Mainnet => Config::parse_peers(self.initial_mainnet_peers.clone()),
|
Network::Mainnet => Config::parse_peers(&self.initial_mainnet_peers).await,
|
||||||
Network::Testnet => Config::parse_peers(self.initial_testnet_peers.clone()),
|
Network::Testnet => Config::parse_peers(&self.initial_testnet_peers).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves `host` into zero or more IP addresses.
|
||||||
|
///
|
||||||
|
/// 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 empty list.
|
||||||
|
async fn resolve_host(host: &str) -> HashSet<SocketAddr> {
|
||||||
|
let fut = tokio::net::lookup_host(host);
|
||||||
|
let fut = tokio::time::timeout(crate::constants::DNS_LOOKUP_TIMEOUT, fut);
|
||||||
|
|
||||||
|
match fut.await {
|
||||||
|
Ok(Ok(ips)) => ips.collect(),
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
tracing::info!(?host, ?e, "DNS error resolving peer IP address");
|
||||||
|
HashSet::new()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::info!(?host, ?e, "DNS timeout resolving peer IP address");
|
||||||
|
HashSet::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,15 @@ lazy_static! {
|
||||||
}.expect("regex is valid");
|
}.expect("regex is valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The timeout for DNS lookups.
|
||||||
|
///
|
||||||
|
/// [6.1.3.3 Efficient Resource Usage] from [RFC 1123: Requirements for Internet Hosts]
|
||||||
|
/// suggest no less than 5 seconds for resolving timeout.
|
||||||
|
///
|
||||||
|
/// [RFC 1123: Requirements for Internet Hosts] https://tools.ietf.org/rfcmarkup?doc=1123
|
||||||
|
/// [6.1.3.3 Efficient Resource Usage] https://tools.ietf.org/rfcmarkup?doc=1123#page-77
|
||||||
|
pub const DNS_LOOKUP_TIMEOUT: Duration = Duration::from_secs(5);
|
||||||
|
|
||||||
/// Magic numbers used to identify different Zcash networks.
|
/// Magic numbers used to identify different Zcash networks.
|
||||||
pub mod magics {
|
pub mod magics {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -135,16 +135,19 @@ where
|
||||||
|
|
||||||
let listen_guard = tokio::spawn(listen(config.listen_addr, listener, peerset_tx.clone()));
|
let listen_guard = tokio::spawn(listen(config.listen_addr, listener, peerset_tx.clone()));
|
||||||
|
|
||||||
|
// 2. Initial peers, specified in the config.
|
||||||
let initial_peers_fut = {
|
let initial_peers_fut = {
|
||||||
let initial_peers = config.initial_peers();
|
let config = config.clone();
|
||||||
let connector = connector.clone();
|
let connector = connector.clone();
|
||||||
let tx = peerset_tx.clone();
|
let peerset_tx = peerset_tx.clone();
|
||||||
|
async move {
|
||||||
// Connect the tx end to the 3 peer sources:
|
let initial_peers = config.initial_peers().await;
|
||||||
add_initial_peers(initial_peers, connector, tx)
|
// Connect the tx end to the 3 peer sources:
|
||||||
|
add_initial_peers(initial_peers, connector, peerset_tx).await
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. Initial peers, specified in the config.
|
|
||||||
let add_guard = tokio::spawn(initial_peers_fut);
|
let add_guard = tokio::spawn(initial_peers_fut);
|
||||||
|
|
||||||
// 3. Outgoing peers we connect to in response to load.
|
// 3. Outgoing peers we connect to in response to load.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue