Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions iroh-cli/src/commands/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use iroh::{
bytes::store::ReadableStore,
net::{
defaults::DEFAULT_RELAY_STUN_PORT,
dns::default_resolver,
key::{PublicKey, SecretKey},
magic_endpoint,
magicsock::EndpointInfo,
Expand Down Expand Up @@ -275,7 +276,8 @@ async fn report(
config: &NodeConfig,
) -> anyhow::Result<()> {
let port_mapper = portmapper::Client::default();
let mut client = netcheck::Client::new(Some(port_mapper))?;
let dns_resolver = default_resolver().clone();
let mut client = netcheck::Client::new(Some(port_mapper), dns_resolver)?;

let dm = match stun_host {
Some(host_name) => {
Expand Down Expand Up @@ -761,10 +763,12 @@ async fn relay_urls(count: usize, config: NodeConfig) -> anyhow::Result<()> {
println!("No relay nodes specified in the config file.");
}

let dns_resolver = default_resolver();
let mut clients = HashMap::new();
for node in &config.relay_nodes {
let secret_key = key.clone();
let client = iroh::net::relay::http::ClientBuilder::new(node.url.clone()).build(secret_key);
let client = iroh::net::relay::http::ClientBuilder::new(node.url.clone())
.build(secret_key, dns_resolver.clone());

clients.insert(node.url.clone(), client);
}
Expand Down
6 changes: 4 additions & 2 deletions iroh-net/src/bin/iroh-relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -954,8 +954,9 @@ mod tests {
// set up clients
let a_secret_key = SecretKey::generate();
let a_key = a_secret_key.public();
let resolver = iroh_net::dns::default_resolver().clone();
let (client_a, mut client_a_receiver) =
ClientBuilder::new(relay_server_url.clone()).build(a_secret_key);
ClientBuilder::new(relay_server_url.clone()).build(a_secret_key, resolver);
let connect_client = client_a.clone();

// give the relay server some time to set up
Expand All @@ -977,8 +978,9 @@ mod tests {

let b_secret_key = SecretKey::generate();
let b_key = b_secret_key.public();
let resolver = iroh_net::dns::default_resolver().clone();
let (client_b, mut client_b_receiver) =
ClientBuilder::new(relay_server_url.clone()).build(b_secret_key);
ClientBuilder::new(relay_server_url.clone()).build(b_secret_key, resolver);
client_b.connect().await?;

let msg = Bytes::from("hello, b");
Expand Down
39 changes: 29 additions & 10 deletions iroh-net/src/dns.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
//! This module exports a DNS resolver, which is also the default resolver used in the
//! [`crate::MagicEndpoint`] if no custom resolver is configured.

use std::net::{IpAddr, Ipv6Addr};
use std::time::Duration;

use anyhow::Result;
use hickory_resolver::{AsyncResolver, IntoName, TokioAsyncResolver, TryParseIp};
use once_cell::sync::Lazy;

pub static DNS_RESOLVER: Lazy<TokioAsyncResolver> =
Lazy::new(|| get_resolver().expect("unable to create DNS resolver"));
/// The DNS resolver type used throughout `iroh-net`.
pub type DnsResolver = TokioAsyncResolver;

static DNS_RESOLVER: Lazy<TokioAsyncResolver> =
Lazy::new(|| create_default_resolver().expect("unable to create DNS resolver"));

/// Get a reference to the default DNS resolver.
///
/// The default resolver can be cheaply cloned and is shared throughout the running process.
/// It is configured to use the system's DNS configuration.
pub fn default_resolver() -> &'static DnsResolver {
&DNS_RESOLVER
}

/// Deprecated IPv6 site-local anycast addresses still configured by windows.
///
Expand All @@ -27,7 +41,7 @@ const WINDOWS_BAD_SITE_LOCAL_DNS_SERVERS: [IpAddr; 3] = [
/// We first try to read the system's resolver from `/etc/resolv.conf`.
/// This does not work at least on some Androids, therefore we fallback
/// to the default `ResolverConfig` which uses eg. to google's `8.8.8.8` or `8.8.4.4`.
fn get_resolver() -> Result<TokioAsyncResolver> {
fn create_default_resolver() -> Result<TokioAsyncResolver> {
let (system_config, mut options) =
hickory_resolver::system_conf::read_system_conf().unwrap_or_default();

Expand All @@ -54,18 +68,20 @@ fn get_resolver() -> Result<TokioAsyncResolver> {
}

pub(crate) async fn lookup_ipv4<N: IntoName + TryParseIp + Clone>(
resolver: &DnsResolver,
host: N,
timeout: Duration,
) -> Result<Vec<IpAddr>> {
let addrs = tokio::time::timeout(timeout, DNS_RESOLVER.ipv4_lookup(host)).await??;
let addrs = tokio::time::timeout(timeout, resolver.ipv4_lookup(host)).await??;
Ok(addrs.into_iter().map(|ip| IpAddr::V4(ip.0)).collect())
}

pub(crate) async fn lookup_ipv6<N: IntoName + TryParseIp + Clone>(
resolver: &DnsResolver,
host: N,
timeout: Duration,
) -> Result<Vec<IpAddr>> {
let addrs = tokio::time::timeout(timeout, DNS_RESOLVER.ipv6_lookup(host)).await??;
let addrs = tokio::time::timeout(timeout, resolver.ipv6_lookup(host)).await??;
Ok(addrs.into_iter().map(|ip| IpAddr::V6(ip.0)).collect())
}

Expand All @@ -74,12 +90,13 @@ pub(crate) async fn lookup_ipv6<N: IntoName + TryParseIp + Clone>(
/// `LookupIpStrategy::Ipv4AndIpv6` will wait for ipv6 resolution timeout, even if it is
/// not usable on the stack, so we manually query both lookups concurrently and time them out
/// individually.
pub(crate) async fn lookup_ipv4_ipv6<N: IntoName + TryParseIp + Clone>(
pub async fn lookup_ipv4_ipv6<N: IntoName + TryParseIp + Clone>(
resolver: &DnsResolver,
host: N,
timeout: Duration,
) -> Result<Vec<IpAddr>> {
let ipv4 = DNS_RESOLVER.ipv4_lookup(host.clone());
let ipv6 = DNS_RESOLVER.ipv6_lookup(host);
let ipv4 = resolver.ipv4_lookup(host.clone());
let ipv6 = resolver.ipv6_lookup(host);
let ipv4 = tokio::time::timeout(timeout, ipv4);
let ipv6 = tokio::time::timeout(timeout, ipv6);

Expand Down Expand Up @@ -134,7 +151,8 @@ mod tests {
#[cfg_attr(target_os = "windows", ignore = "flaky")]
async fn test_dns_lookup_basic() {
let _logging = iroh_test::logging::setup();
let res = DNS_RESOLVER.lookup_ip(NA_RELAY_HOSTNAME).await.unwrap();
let resolver = default_resolver();
let res = resolver.lookup_ip(NA_RELAY_HOSTNAME).await.unwrap();
let res: Vec<_> = res.iter().collect();
assert!(!res.is_empty());
dbg!(res);
Expand All @@ -144,7 +162,8 @@ mod tests {
#[cfg_attr(target_os = "windows", ignore = "flaky")]
async fn test_dns_lookup_ipv4_ipv6() {
let _logging = iroh_test::logging::setup();
let res = lookup_ipv4_ipv6(NA_RELAY_HOSTNAME, Duration::from_secs(5))
let resolver = default_resolver();
let res = lookup_ipv4_ipv6(resolver, NA_RELAY_HOSTNAME, Duration::from_secs(5))
.await
.unwrap();
assert!(!res.is_empty());
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod defaults;
pub mod dialer;
mod disco;
pub mod discovery;
mod dns;
pub mod dns;
pub mod magic_endpoint;
pub mod magicsock;
pub mod metrics;
Expand Down
20 changes: 20 additions & 0 deletions iroh-net/src/magic_endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
config,
defaults::default_relay_map,
discovery::{Discovery, DiscoveryTask},
dns::{default_resolver, DnsResolver},
key::{PublicKey, SecretKey},
magicsock::{self, MagicSock},
relay::{RelayMap, RelayMode, RelayUrl},
Expand All @@ -39,6 +40,7 @@ pub struct MagicEndpointBuilder {
discovery: Option<Box<dyn Discovery>>,
/// Path for known peers. See [`MagicEndpointBuilder::peers_data_path`].
peers_path: Option<PathBuf>,
dns_resolver: Option<DnsResolver>,
}

impl Default for MagicEndpointBuilder {
Expand All @@ -52,6 +54,7 @@ impl Default for MagicEndpointBuilder {
keylog: Default::default(),
discovery: Default::default(),
peers_path: None,
dns_resolver: None,
}
}
}
Expand Down Expand Up @@ -141,6 +144,18 @@ impl MagicEndpointBuilder {
self
}

/// Optionally set a custom DNS resolver to use for this endpoint.
///
/// The DNS resolver is used to resolve relay hostnames.
///
/// By default, all magic endpoints share a DNS resolver, which is configured to use the
/// host system's DNS configuration. You can pass a custom instance of [`DnsResolver`]
/// here to use a differently configured DNS resolver for this endpoint.
pub fn dns_resolver(mut self, dns_resolver: DnsResolver) -> Self {
self.dns_resolver = Some(dns_resolver);
self
}

/// Bind the magic endpoint on the specified socket address.
///
/// The *bind_port* is the port that should be bound locally.
Expand All @@ -166,12 +181,17 @@ impl MagicEndpointBuilder {
if let Some(c) = self.concurrent_connections {
server_config.concurrent_connections(c);
}
let dns_resolver = self
.dns_resolver
.unwrap_or_else(|| default_resolver().clone());

let msock_opts = magicsock::Options {
port: bind_port,
secret_key,
relay_map,
nodes_path: self.peers_path,
discovery: self.discovery,
dns_resolver,
};
MagicEndpoint::bind(Some(server_config), msock_opts, self.keylog).await
}
Expand Down
18 changes: 15 additions & 3 deletions iroh-net/src/magicsock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use crate::{
config,
disco::{self, SendAddr},
discovery::Discovery,
dns::DNS_RESOLVER,
dns::DnsResolver,
key::{PublicKey, SecretKey, SharedSecret},
magic_endpoint::NodeAddr,
net::{interfaces, ip::LocalAddresses, netmon, IpFamily},
Expand Down Expand Up @@ -113,6 +113,12 @@ pub struct Options {

/// Optional node discovery mechanism.
pub discovery: Option<Box<dyn Discovery>>,

/// A DNS resolver to use for resolving relay URLs.
///
/// You can use [`crate::dns::default_resolver`] for a resolver that uses the system's DNS
/// configuration.
pub dns_resolver: DnsResolver,
}

impl Default for Options {
Expand All @@ -123,6 +129,7 @@ impl Default for Options {
relay_map: RelayMap::empty(),
nodes_path: None,
discovery: None,
dns_resolver: crate::dns::default_resolver().clone(),
}
}
}
Expand Down Expand Up @@ -161,6 +168,9 @@ struct Inner {
network_recv_wakers: parking_lot::Mutex<Option<Waker>>,
network_send_wakers: parking_lot::Mutex<Option<Waker>>,

/// The DNS resolver to be used in this magicsock.
dns_resolver: DnsResolver,

/// Key for this node.
secret_key: SecretKey,

Expand Down Expand Up @@ -1135,6 +1145,7 @@ impl MagicSock {
relay_map,
discovery,
nodes_path,
dns_resolver,
} = opts;

let nodes_path = match nodes_path {
Expand Down Expand Up @@ -1164,7 +1175,7 @@ impl MagicSock {
let ipv4_addr = pconn4.local_addr()?;
let ipv6_addr = pconn6.as_ref().and_then(|c| c.local_addr().ok());

let net_checker = netcheck::Client::new(Some(port_mapper.clone()))?;
let net_checker = netcheck::Client::new(Some(port_mapper.clone()), dns_resolver.clone())?;

let (actor_sender, actor_receiver) = mpsc::channel(256);
let (relay_actor_sender, relay_actor_receiver) = mpsc::channel(256);
Expand Down Expand Up @@ -1214,6 +1225,7 @@ impl MagicSock {
endpoints: Watchable::new(Default::default()),
pending_call_me_maybes: Default::default(),
endpoints_update_state: EndpointUpdateState::new(),
dns_resolver,
});

let mut actor_tasks = JoinSet::default();
Expand Down Expand Up @@ -1696,7 +1708,7 @@ impl Actor {
debug!("link change detected: major? {}", is_major);

if is_major {
DNS_RESOLVER.clear_cache();
self.inner.dns_resolver.clear_cache();
self.inner.re_stun("link-change-major");
self.close_stale_relay_connections().await;
self.reset_endpoint_states();
Expand Down
2 changes: 1 addition & 1 deletion iroh-net/src/magicsock/relay_actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ impl RelayActor {
})
.can_ack_pings(true)
.is_preferred(my_relay.as_ref() == Some(&url1))
.build(self.conn.secret_key.clone());
.build(self.conn.secret_key.clone(), self.conn.dns_resolver.clone());

let (s, r) = mpsc::channel(64);

Expand Down
Loading