@@ -27,17 +27,62 @@ pub const N0_DNS_NODE_ORIGIN_PROD: &str = "dns.iroh.link";
27
27
/// The n0 testing DNS node origin, for testing.
28
28
pub const N0_DNS_NODE_ORIGIN_STAGING : & str = "staging-dns.iroh.link" ;
29
29
30
- /// The default DNS resolver used throughout `iroh`.
30
+ /// Trait for DNS resolvers used in iroh.
31
+ pub trait Resolver : fmt:: Debug + Send + Sync + ' static {
32
+ /// Looks up an IPv4 address.
33
+ fn lookup_ipv4 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv4Addr > > > ;
34
+
35
+ /// Looks up an IPv6 address.
36
+ fn lookup_ipv6 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv6Addr > > > ;
37
+
38
+ /// Looks up TXT records.
39
+ fn lookup_txt ( & self , host : String ) -> BoxFuture < Result < BoxIter < TxtRecord > > > ;
40
+
41
+ /// Clears the internal cache.
42
+ fn clear_cache ( & self ) ;
43
+ }
44
+
45
+ /// Boxed iterator alias.
46
+ pub type BoxIter < T > = Box < dyn Iterator < Item = T > + Send + ' static > ;
47
+
48
+ /// DNS resolver for use in iroh.
49
+ ///
50
+ /// This internally contains a [`dyn Resolver`]. See the public methods for how to construct
51
+ /// a [`DnsResolver`] with sensible defaults or with a custom resolver.
31
52
#[ derive( Debug , Clone ) ]
32
- struct HickoryResolver ( TokioResolver ) ;
53
+ pub struct DnsResolver ( Arc < dyn Resolver > ) ;
33
54
34
- impl HickoryResolver {
35
- /// Create a new DNS resolver with sensible cross-platform defaults.
55
+ impl std:: ops:: Deref for DnsResolver {
56
+ type Target = dyn Resolver ;
57
+ fn deref ( & self ) -> & Self :: Target {
58
+ & * self . 0
59
+ }
60
+ }
61
+
62
+ impl Default for DnsResolver {
63
+ fn default ( ) -> Self {
64
+ Self :: new_with_system_defaults ( )
65
+ }
66
+ }
67
+
68
+ impl DnsResolver {
69
+ /// Creates a new [`DnsResolver`] from a struct that implements [`Resolver`].
70
+ ///
71
+ /// [`Resolver`] is implemented for [`hickory_resolver::TokioResolver`], so you can construct
72
+ /// a [`TokioResolver`] and pass that to this function.
73
+ ///
74
+ /// To use a different DNS resolver, you need to implement [`Resolver`] for your custom resolver
75
+ /// and then pass to this function.
76
+ pub fn new ( resolver : impl Resolver ) -> Self {
77
+ Self ( Arc :: new ( resolver) )
78
+ }
79
+
80
+ /// Creates a new DNS resolver with sensible cross-platform defaults.
36
81
///
37
82
/// We first try to read the system's resolver from `/etc/resolv.conf`.
38
83
/// This does not work at least on some Androids, therefore we fallback
39
- /// to the default `ResolverConfig` which uses eg. to google 's `8.8.8.8` or `8.8.4.4`.
40
- fn new ( ) -> Self {
84
+ /// to the default `ResolverConfig` which uses Google 's `8.8.8.8` or `8.8.4.4`.
85
+ pub fn new_with_system_defaults ( ) -> Self {
41
86
let ( system_config, mut options) =
42
87
hickory_resolver:: system_conf:: read_system_conf ( ) . unwrap_or_default ( ) ;
43
88
@@ -62,11 +107,11 @@ impl HickoryResolver {
62
107
let mut builder =
63
108
TokioResolver :: builder_with_config ( config, TokioConnectionProvider :: default ( ) ) ;
64
109
* builder. options_mut ( ) = options;
65
- HickoryResolver ( builder. build ( ) )
110
+ Self :: new ( builder. build ( ) )
66
111
}
67
112
68
- /// Create a new DNS resolver configured with a single UDP DNS nameserver.
69
- fn with_nameserver ( nameserver : SocketAddr ) -> Self {
113
+ /// Creates a new DNS resolver configured with a single UDP DNS nameserver.
114
+ pub fn with_nameserver ( nameserver : SocketAddr ) -> Self {
70
115
let mut config = hickory_resolver:: config:: ResolverConfig :: new ( ) ;
71
116
let nameserver_config = hickory_resolver:: config:: NameServerConfig :: new (
72
117
nameserver,
@@ -76,108 +121,10 @@ impl HickoryResolver {
76
121
77
122
let builder =
78
123
TokioResolver :: builder_with_config ( config, TokioConnectionProvider :: default ( ) ) ;
79
- HickoryResolver ( builder. build ( ) )
124
+ Self :: new ( builder. build ( ) )
80
125
}
81
- }
82
126
83
- impl Default for HickoryResolver {
84
- fn default ( ) -> Self {
85
- Self :: new ( )
86
- }
87
- }
88
-
89
- /// Trait for DNS resolvers used in iroh.
90
- pub trait Resolver : std:: fmt:: Debug + Send + Sync + ' static {
91
- /// Lookup an IPv4 address.
92
- fn lookup_ipv4 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv4Addr > > > ;
93
- /// Lookup an IPv6 address.
94
- fn lookup_ipv6 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv6Addr > > > ;
95
- /// Lookup TXT records.
96
- fn lookup_txt ( & self , host : String ) -> BoxFuture < Result < BoxIter < TxtRecord > > > ;
97
- /// Clear the internal cache.
98
- fn clear_cache ( & self ) ;
99
- }
100
-
101
- /// Boxed iterator alias.
102
- pub type BoxIter < T > = Box < dyn Iterator < Item = T > + Send + ' static > ;
103
-
104
- impl Resolver for HickoryResolver {
105
- fn lookup_ipv4 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv4Addr > > > {
106
- let this = self . 0 . clone ( ) ;
107
- Box :: pin ( async move {
108
- let addrs = this. ipv4_lookup ( host) . await ?;
109
- let iter: BoxIter < Ipv4Addr > = Box :: new ( addrs. into_iter ( ) . map ( Ipv4Addr :: from) ) ;
110
- Ok ( iter)
111
- } )
112
- }
113
-
114
- fn lookup_ipv6 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv6Addr > > > {
115
- let this = self . 0 . clone ( ) ;
116
- Box :: pin ( async move {
117
- let addrs = this. ipv6_lookup ( host) . await ?;
118
- let iter: BoxIter < Ipv6Addr > = Box :: new ( addrs. into_iter ( ) . map ( Ipv6Addr :: from) ) ;
119
- Ok ( iter)
120
- } )
121
- }
122
-
123
- fn lookup_txt ( & self , host : String ) -> BoxFuture < Result < BoxIter < TxtRecord > > > {
124
- let this = self . 0 . clone ( ) ;
125
- Box :: pin ( async move {
126
- let lookup = this. txt_lookup ( host) . await ?;
127
- let iter: BoxIter < TxtRecord > = Box :: new (
128
- lookup
129
- . into_iter ( )
130
- . map ( |txt| TxtRecord :: from_iter ( txt. iter ( ) . cloned ( ) ) ) ,
131
- ) ;
132
- Ok ( iter)
133
- } )
134
- }
135
-
136
- fn clear_cache ( & self ) {
137
- self . 0 . clear_cache ( ) ;
138
- }
139
- }
140
-
141
- impl From < TokioResolver > for HickoryResolver {
142
- fn from ( resolver : TokioResolver ) -> Self {
143
- HickoryResolver ( resolver)
144
- }
145
- }
146
-
147
- ///
148
- #[ derive( Debug , Clone ) ]
149
- pub struct DnsResolver ( Arc < dyn Resolver > ) ;
150
-
151
- impl std:: ops:: Deref for DnsResolver {
152
- type Target = dyn Resolver ;
153
- fn deref ( & self ) -> & Self :: Target {
154
- & * self . 0
155
- }
156
- }
157
-
158
- impl Default for DnsResolver {
159
- fn default ( ) -> Self {
160
- Self :: new_with_system_defaults ( )
161
- }
162
- }
163
-
164
- impl DnsResolver {
165
- ///
166
- pub fn new ( resolver : impl Resolver ) -> Self {
167
- Self ( Arc :: new ( resolver) )
168
- }
169
-
170
- ///
171
- pub fn new_with_system_defaults ( ) -> Self {
172
- Self :: new ( HickoryResolver :: new ( ) )
173
- }
174
-
175
- ///
176
- pub fn with_nameserver ( nameserver : SocketAddr ) -> Self {
177
- Self :: new ( HickoryResolver :: with_nameserver ( nameserver) )
178
- }
179
-
180
- /// Lookup a TXT record.
127
+ /// Performs a TXT lookup with a timeout.
181
128
pub async fn lookup_txt (
182
129
& self ,
183
130
host : impl ToString ,
@@ -187,7 +134,7 @@ impl DnsResolver {
187
134
Ok ( res)
188
135
}
189
136
190
- /// Perform an ipv4 lookup with a timeout.
137
+ /// Performs an IPv4 lookup with a timeout.
191
138
pub async fn lookup_ipv4 (
192
139
& self ,
193
140
host : impl ToString ,
@@ -197,7 +144,7 @@ impl DnsResolver {
197
144
Ok ( addrs. map ( |ip| IpAddr :: V4 ( ip) ) )
198
145
}
199
146
200
- /// Perform an ipv6 lookup with a timeout.
147
+ /// Performs an IPv6 lookup with a timeout.
201
148
pub async fn lookup_ipv6 (
202
149
& self ,
203
150
host : impl ToString ,
@@ -207,7 +154,7 @@ impl DnsResolver {
207
154
Ok ( addrs. map ( |ip| IpAddr :: V6 ( ip) ) )
208
155
}
209
156
210
- /// Resolve IPv4 and IPv6 in parallel with a timeout.
157
+ /// Resolves IPv4 and IPv6 in parallel with a timeout.
211
158
///
212
159
/// `LookupIpStrategy::Ipv4AndIpv6` will wait for ipv6 resolution timeout, even if it is
213
160
/// not usable on the stack, so we manually query both lookups concurrently and time them out
@@ -233,7 +180,7 @@ impl DnsResolver {
233
180
}
234
181
}
235
182
236
- /// Resolve a hostname from a URL to an IP address.
183
+ /// Resolves a hostname from a URL to an IP address.
237
184
pub async fn resolve_host (
238
185
& self ,
239
186
url : & Url ,
@@ -263,7 +210,7 @@ impl DnsResolver {
263
210
}
264
211
}
265
212
266
- /// Perform an ipv4 lookup with a timeout in a staggered fashion.
213
+ /// Performs an IPv4 lookup with a timeout in a staggered fashion.
267
214
///
268
215
/// From the moment this function is called, each lookup is scheduled after the delays in
269
216
/// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
@@ -280,7 +227,7 @@ impl DnsResolver {
280
227
stagger_call ( f, delays_ms) . await
281
228
}
282
229
283
- /// Perform an ipv6 lookup with a timeout in a staggered fashion.
230
+ /// Performs an IPv6 lookup with a timeout in a staggered fashion.
284
231
///
285
232
/// From the moment this function is called, each lookup is scheduled after the delays in
286
233
/// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
@@ -297,7 +244,7 @@ impl DnsResolver {
297
244
stagger_call ( f, delays_ms) . await
298
245
}
299
246
300
- /// Race an ipv4 and ipv6 lookup with a timeout in a staggered fashion.
247
+ /// Races an IPv4 and IPv6 lookup with a timeout in a staggered fashion.
301
248
///
302
249
/// From the moment this function is called, each lookup is scheduled after the delays in
303
250
/// `delays_ms` with the first call being done immediately. `[200ms, 300ms]` results in calls
@@ -367,24 +314,66 @@ impl DnsResolver {
367
314
}
368
315
}
369
316
317
+ /// Implementation of [`Resolver`] for [`hickory_resolver::TokioResolver`].
318
+ impl Resolver for TokioResolver {
319
+ fn lookup_ipv4 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv4Addr > > > {
320
+ let this = self . clone ( ) ;
321
+ Box :: pin ( async move {
322
+ let addrs = this. ipv4_lookup ( host) . await ?;
323
+ let iter: BoxIter < Ipv4Addr > = Box :: new ( addrs. into_iter ( ) . map ( Ipv4Addr :: from) ) ;
324
+ Ok ( iter)
325
+ } )
326
+ }
327
+
328
+ fn lookup_ipv6 ( & self , host : String ) -> BoxFuture < Result < BoxIter < Ipv6Addr > > > {
329
+ let this = self . clone ( ) ;
330
+ Box :: pin ( async move {
331
+ let addrs = this. ipv6_lookup ( host) . await ?;
332
+ let iter: BoxIter < Ipv6Addr > = Box :: new ( addrs. into_iter ( ) . map ( Ipv6Addr :: from) ) ;
333
+ Ok ( iter)
334
+ } )
335
+ }
336
+
337
+ fn lookup_txt ( & self , host : String ) -> BoxFuture < Result < BoxIter < TxtRecord > > > {
338
+ let this = self . clone ( ) ;
339
+ Box :: pin ( async move {
340
+ let lookup = this. txt_lookup ( host) . await ?;
341
+ let iter: BoxIter < TxtRecord > = Box :: new (
342
+ lookup
343
+ . into_iter ( )
344
+ . map ( |txt| TxtRecord :: from_iter ( txt. iter ( ) . cloned ( ) ) ) ,
345
+ ) ;
346
+ Ok ( iter)
347
+ } )
348
+ }
349
+
350
+ fn clear_cache ( & self ) {
351
+ self . clear_cache ( ) ;
352
+ }
353
+ }
354
+
370
355
/// Record data for a TXT record.
356
+ ///
357
+ /// This contains a list of character strings, as defined in [RFC 1035 Section 3.3.14].
358
+ ///
359
+ /// [`TxtRecord`] implements [`fmt::Display`], so you can call [`ToString::to_string`] to
360
+ /// convert the record data into a string. This will parse each character string with
361
+ /// [`String::from_utf8_lossy`] and then concatenate all strings without a seperator.
362
+ ///
363
+ /// If you want to process each character string individually, use [`Self::iter`].
364
+ ///
365
+ /// [RFC 1035 Section 3.3.14]: https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
371
366
#[ derive( Debug , Clone ) ]
372
- pub struct TxtRecord ( Vec < Box < [ u8 ] > > ) ;
367
+ pub struct TxtRecord ( Box < [ Box < [ u8 ] > ] > ) ;
373
368
374
369
impl TxtRecord {
375
- ///
370
+ /// Returns an iterator over the character strings contained in this TXT record.
376
371
pub fn iter ( & self ) -> impl Iterator < Item = & [ u8 ] > {
377
372
self . 0 . iter ( ) . map ( |x| x. as_ref ( ) )
378
373
}
379
-
380
- ///
381
- pub fn to_strings ( & self ) -> impl Iterator < Item = String > + ' _ {
382
- self . iter ( )
383
- . map ( |cstr| String :: from_utf8_lossy ( & cstr) . to_string ( ) )
384
- }
385
374
}
386
375
387
- impl std :: fmt:: Display for TxtRecord {
376
+ impl fmt:: Display for TxtRecord {
388
377
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
389
378
for s in self . iter ( ) {
390
379
write ! ( f, "{}" , & String :: from_utf8_lossy( s) ) ?
@@ -399,6 +388,12 @@ impl FromIterator<Box<[u8]>> for TxtRecord {
399
388
}
400
389
}
401
390
391
+ impl From < Vec < Box < [ u8 ] > > > for TxtRecord {
392
+ fn from ( value : Vec < Box < [ u8 ] > > ) -> Self {
393
+ Self ( value. into_boxed_slice ( ) )
394
+ }
395
+ }
396
+
402
397
/// Deprecated IPv6 site-local anycast addresses still configured by windows.
403
398
///
404
399
/// Windows still configures these site-local addresses as soon even as an IPv6 loopback
0 commit comments