Skip to content

Commit c39b998

Browse files
authored
refactor!: use a single DNS resolver (#3167)
## Description We used to have more-or-less identical code in various places to set a static DNS resolver and add a few extension methods on the `hickory_resolver::TokioResolver`. This PR cleans this up and makes all of iroh use the same resolver, defined in a single place, as a newtype struct around the `TokioResolver` from `hickory_resolver`: * Adds a newtype struct `DnsResolver` around `hickory_resolver::TokioResolver` in `iroh_relay::dns`. It lives (for now) in `iroh_relay`, because this is a the bottom of our dependency chain: both `iroh` and `iroh-net-report` need to use it as well, and both depend on `iroh_relay`. * Adds most methods from the previous extension traits to the `DnsResolver` struct * Changes all imports to use this `DnsResolver` now instead of the previous type alias to TokioResolver * Removes all now-obsolete duplicate extension traits and type aliases and duplicated tests * Removes the global `static` DnsResolver that was accessible via `iroh::dns::default_resolver`. This means that by default multiple endpoints in the same app would not share the DnsResolver. However, I'd say a) it is not too common to use multiple endpoints, and b) if you do and want to share the DnsResolver, you can still do so by creating the DnsResolver yourself and cloning it into the endpoint builder. * Cleans up and aligns the various methods on `DnsResolver`, and newtype the types from hickory so that this public iroh API does not depend on hickory types (apart from a `From<TokioResolver> for DnsResolver`, which is deferred to a follow-up to investigate) The only place outside of tests and examples where a new `DnsResolver` is constructed is in endpoint builder if no custom DnsResolver is set. This means: As long as an app has a single endpoint only, there will only be one DnsResolver. ## Breaking Changes * `iroh::dns::resolver` and `iroh::dns::default_resolver` are removed. There is no static, global DNS resolver anymore. If you want to share a DNS resolver between endpoints, create the resolver yourself with `iroh::dns::DnsResolver::new` and clone it into the endpoint builders (in `EndpointBuilder::dns_resolver`). If you want to reuse the DNS resolver from an endpoint, you can access it with `Endpoint::dns_resolver` and clone it to wherever you need it. * `iroh::dns::DnsResolver` used to be a type alias and now is a reexport of `iroh_relay::dns::DnsResolver` struct * `iroh::dns::node_info` module is now a reexport of `iroh_relay::dns::node_info` * `iroh::dns::node_info::{IrohAttr, TxtAttrs, node_id_from_hickory_name}` are no longer public. Use `iroh::dns::DnsResolver::lookup_node_by_id` or `iroh::dns::DnsResolver::lookup_node_by_domain_name` to lookup node info from DNS. * `iroh::dns::node_info::{to_z32, from_z32}`are removed. Use the methods on `iroh::dns::node_info::NodeIdExt` trait instead. * `iroh::discovery::dns::{N0_DNS_NODE_ORIGIN_PROD, N0_DNS_NODE_ORIGIN_STAGING}` are now reexports of `iroh_relay::dns::{N0_DNS_NODE_ORIGIN_PROD, N0_DNS_NODE_ORIGIN_STAGING}` * `iroh::dns::ResolverExt` is removed. Use the methods on `iroh::dns::DnsResolver` instead. * The methods in `iroh::dns::DnsResolver` now take an `impl ToString` instead of `impl hickory_proto::rr::domain::IntoName` for their `host` argument * `iroh::test_utils::create_dns_resolver` is removed, use `iroh::dns::DnsResolver::new` instead ## Notes & open questions <!-- Any notes, remarks or open questions you have to make about the PR. --> ## Change checklist - [x] Self-review. - [x] Documentation updates following the [style guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text), if relevant. - [x] All breaking changes documented.
1 parent d43d474 commit c39b998

File tree

22 files changed

+633
-947
lines changed

22 files changed

+633
-947
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh-dns-server/examples/publish.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use iroh::{
77
dns::{N0_DNS_NODE_ORIGIN_PROD, N0_DNS_NODE_ORIGIN_STAGING},
88
pkarr::{PkarrRelayClient, N0_DNS_PKARR_RELAY_PROD, N0_DNS_PKARR_RELAY_STAGING},
99
},
10-
dns::node_info::{to_z32, NodeInfo, IROH_TXT_NAME},
10+
dns::node_info::{NodeIdExt, NodeInfo, IROH_TXT_NAME},
1111
NodeId, SecretKey,
1212
};
1313
use url::Url;
@@ -117,5 +117,5 @@ async fn main() -> Result<()> {
117117
}
118118

119119
fn fmt_domain(node_id: &NodeId, origin: &str) -> String {
120-
format!("{}.{}.{}", IROH_TXT_NAME, to_z32(node_id), origin)
120+
format!("{}.{}.{}", IROH_TXT_NAME, node_id.to_z32(), origin)
121121
}

iroh-dns-server/examples/resolve.rs

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
1-
use std::net::SocketAddr;
2-
31
use clap::{Parser, ValueEnum};
4-
use hickory_resolver::{
5-
config::{NameServerConfig, ResolverConfig},
6-
proto::xfer::Protocol,
7-
Resolver,
8-
};
92
use iroh::{
103
discovery::dns::{N0_DNS_NODE_ORIGIN_PROD, N0_DNS_NODE_ORIGIN_STAGING},
11-
dns::{node_info::TxtAttrs, DnsResolver},
4+
dns::DnsResolver,
125
NodeId,
136
};
147

@@ -46,39 +39,23 @@ enum Command {
4639
async fn main() -> anyhow::Result<()> {
4740
let args = Cli::parse();
4841
let (resolver, origin) = match args.env {
49-
Env::Staging => (
50-
iroh::dns::default_resolver().clone(),
51-
N0_DNS_NODE_ORIGIN_STAGING,
52-
),
53-
Env::Prod => (
54-
iroh::dns::default_resolver().clone(),
55-
N0_DNS_NODE_ORIGIN_PROD,
56-
),
42+
Env::Staging => (DnsResolver::new(), N0_DNS_NODE_ORIGIN_STAGING),
43+
Env::Prod => (DnsResolver::new(), N0_DNS_NODE_ORIGIN_PROD),
5744
Env::Dev => (
58-
resolver_with_nameserver(LOCALHOST_DNS.parse()?),
45+
DnsResolver::with_nameserver(LOCALHOST_DNS.parse()?),
5946
EXAMPLE_ORIGIN,
6047
),
6148
};
6249
let resolved = match args.command {
63-
Command::Node { node_id } => {
64-
TxtAttrs::<String>::lookup_by_id(&resolver, &node_id, origin).await?
65-
}
66-
Command::Domain { domain } => {
67-
TxtAttrs::<String>::lookup_by_name(&resolver, &domain).await?
68-
}
50+
Command::Node { node_id } => resolver.lookup_node_by_id(&node_id, origin).await?,
51+
Command::Domain { domain } => resolver.lookup_node_by_domain_name(&domain).await?,
6952
};
70-
println!("resolved node {}", resolved.node_id());
71-
for (key, values) in resolved.attrs() {
72-
for value in values {
73-
println!(" {key}={value}");
74-
}
53+
println!("resolved node {}", resolved.node_id);
54+
if let Some(url) = resolved.relay_url {
55+
println!(" relay={url}")
56+
}
57+
for addr in resolved.direct_addresses {
58+
println!(" addr={addr}")
7559
}
7660
Ok(())
7761
}
78-
79-
fn resolver_with_nameserver(nameserver: SocketAddr) -> DnsResolver {
80-
let mut config = ResolverConfig::new();
81-
let nameserver_config = NameServerConfig::new(nameserver, Protocol::Udp);
82-
config.add_name_server(nameserver_config);
83-
Resolver::tokio(config, Default::default())
84-
}

iroh-dns-server/src/lib.rs

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,9 @@ mod tests {
2222
};
2323

2424
use anyhow::Result;
25-
use hickory_resolver::{
26-
config::{NameServerConfig, ResolverConfig},
27-
Resolver,
28-
};
29-
use hickory_server::proto::xfer::Protocol;
3025
use iroh::{
3126
discovery::pkarr::PkarrRelayClient,
32-
dns::{node_info::NodeInfo, DnsResolver, ResolverExt},
27+
dns::{node_info::NodeInfo, DnsResolver},
3328
SecretKey,
3429
};
3530
use pkarr::{PkarrClient, SignedPacket};
@@ -45,6 +40,8 @@ mod tests {
4540
ZoneStore,
4641
};
4742

43+
const DNS_TIMEOUT: Duration = Duration::from_secs(1);
44+
4845
#[tokio::test]
4946
#[traced_test]
5047
async fn pkarr_publish_dns_resolve() -> Result<()> {
@@ -120,38 +117,38 @@ mod tests {
120117

121118
// resolve root record
122119
let name = Name::from_utf8(format!("{pubkey}."))?;
123-
let res = resolver.txt_lookup(name).await?;
124-
let records = res.iter().map(|t| t.to_string()).collect::<Vec<_>>();
120+
let res = resolver.lookup_txt(name, DNS_TIMEOUT).await?;
121+
let records = res.into_iter().map(|t| t.to_string()).collect::<Vec<_>>();
125122
assert_eq!(records, vec!["hi0".to_string()]);
126123

127124
// resolve level one record
128125
let name = Name::from_utf8(format!("_hello.{pubkey}."))?;
129-
let res = resolver.txt_lookup(name).await?;
130-
let records = res.iter().map(|t| t.to_string()).collect::<Vec<_>>();
126+
let res = resolver.lookup_txt(name, DNS_TIMEOUT).await?;
127+
let records = res.into_iter().map(|t| t.to_string()).collect::<Vec<_>>();
131128
assert_eq!(records, vec!["hi1".to_string()]);
132129

133130
// resolve level two record
134131
let name = Name::from_utf8(format!("_hello.world.{pubkey}."))?;
135-
let res = resolver.txt_lookup(name).await?;
136-
let records = res.iter().map(|t| t.to_string()).collect::<Vec<_>>();
132+
let res = resolver.lookup_txt(name, DNS_TIMEOUT).await?;
133+
let records = res.into_iter().map(|t| t.to_string()).collect::<Vec<_>>();
137134
assert_eq!(records, vec!["hi2".to_string()]);
138135

139136
// resolve multiple records for same name
140137
let name = Name::from_utf8(format!("multiple.{pubkey}."))?;
141-
let res = resolver.txt_lookup(name).await?;
142-
let records = res.iter().map(|t| t.to_string()).collect::<Vec<_>>();
138+
let res = resolver.lookup_txt(name, DNS_TIMEOUT).await?;
139+
let records = res.into_iter().map(|t| t.to_string()).collect::<Vec<_>>();
143140
assert_eq!(records, vec!["hi3".to_string(), "hi4".to_string()]);
144141

145142
// resolve A record
146143
let name = Name::from_utf8(format!("{pubkey}."))?;
147-
let res = resolver.ipv4_lookup(name).await?;
148-
let records = res.iter().map(|t| t.0).collect::<Vec<_>>();
144+
let res = resolver.lookup_ipv4(name, DNS_TIMEOUT).await?;
145+
let records = res.collect::<Vec<_>>();
149146
assert_eq!(records, vec![Ipv4Addr::LOCALHOST]);
150147

151148
// resolve AAAA record
152149
let name = Name::from_utf8(format!("foo.bar.baz.{pubkey}."))?;
153-
let res = resolver.ipv6_lookup(name).await?;
154-
let records = res.iter().map(|t| t.0).collect::<Vec<_>>();
150+
let res = resolver.lookup_ipv6(name, DNS_TIMEOUT).await?;
151+
let records = res.collect::<Vec<_>>();
155152
assert_eq!(records, vec![Ipv6Addr::LOCALHOST]);
156153

157154
server.shutdown().await?;
@@ -181,7 +178,7 @@ mod tests {
181178
pkarr.publish(&signed_packet).await?;
182179

183180
let resolver = test_resolver(nameserver);
184-
let res = resolver.lookup_by_id(&node_id, origin).await?;
181+
let res = resolver.lookup_node_by_id(&node_id, origin).await?;
185182

186183
assert_eq!(res.node_id, node_id);
187184
assert_eq!(res.relay_url.map(Url::from), Some(relay_url));
@@ -252,7 +249,7 @@ mod tests {
252249

253250
// resolve via DNS from our server, which will lookup from our DHT
254251
let resolver = test_resolver(nameserver);
255-
let res = resolver.lookup_by_id(&node_id, origin).await?;
252+
let res = resolver.lookup_node_by_id(&node_id, origin).await?;
256253

257254
assert_eq!(res.node_id, node_id);
258255
assert_eq!(res.relay_url.map(Url::from), Some(relay_url));
@@ -265,10 +262,7 @@ mod tests {
265262
}
266263

267264
fn test_resolver(nameserver: SocketAddr) -> DnsResolver {
268-
let mut config = ResolverConfig::new();
269-
let nameserver_config = NameServerConfig::new(nameserver, Protocol::Udp);
270-
config.add_name_server(nameserver_config);
271-
Resolver::tokio(config, Default::default())
265+
DnsResolver::with_nameserver(nameserver)
272266
}
273267

274268
fn random_signed_packet() -> Result<SignedPacket> {

0 commit comments

Comments
 (0)