3
3
use std:: {
4
4
fmt:: { self , Write } ,
5
5
future:: Future ,
6
- net:: { IpAddr , Ipv6Addr , SocketAddr } ,
6
+ net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr } ,
7
+ sync:: Arc ,
7
8
} ;
8
9
9
10
use anyhow:: { bail, Context , Result } ;
10
11
use hickory_resolver:: { name_server:: TokioConnectionProvider , TokioResolver } ;
11
12
use iroh_base:: NodeId ;
12
13
use n0_future:: {
14
+ boxed:: BoxFuture ,
13
15
time:: { self , Duration } ,
14
16
StreamExt ,
15
17
} ;
16
18
use url:: Url ;
17
19
18
- use crate :: node_info:: NodeInfo ;
20
+ use crate :: {
21
+ defaults:: timeouts:: DNS_TIMEOUT ,
22
+ node_info:: { self , NodeInfo } ,
23
+ } ;
19
24
20
25
/// The n0 testing DNS node origin, for production.
21
26
pub const N0_DNS_NODE_ORIGIN_PROD : & str = "dns.iroh.link" ;
22
27
/// The n0 testing DNS node origin, for testing.
23
28
pub const N0_DNS_NODE_ORIGIN_STAGING : & str = "staging-dns.iroh.link" ;
24
29
25
- /// The DNS resolver used throughout `iroh`.
30
+ /// The default DNS resolver used throughout `iroh`.
26
31
#[ derive( Debug , Clone ) ]
27
- pub struct DnsResolver ( TokioResolver ) ;
32
+ struct HickoryResolver ( TokioResolver ) ;
28
33
29
- impl DnsResolver {
34
+ impl HickoryResolver {
30
35
/// Create a new DNS resolver with sensible cross-platform defaults.
31
36
///
32
37
/// We first try to read the system's resolver from `/etc/resolv.conf`.
33
38
/// This does not work at least on some Androids, therefore we fallback
34
39
/// to the default `ResolverConfig` which uses eg. to google's `8.8.8.8` or `8.8.4.4`.
35
- pub fn new ( ) -> Self {
40
+ fn new ( ) -> Self {
36
41
let ( system_config, mut options) =
37
42
hickory_resolver:: system_conf:: read_system_conf ( ) . unwrap_or_default ( ) ;
38
43
@@ -57,11 +62,11 @@ impl DnsResolver {
57
62
let mut builder =
58
63
TokioResolver :: builder_with_config ( config, TokioConnectionProvider :: default ( ) ) ;
59
64
* builder. options_mut ( ) = options;
60
- DnsResolver ( builder. build ( ) )
65
+ HickoryResolver ( builder. build ( ) )
61
66
}
62
67
63
68
/// Create a new DNS resolver configured with a single UDP DNS nameserver.
64
- pub fn with_nameserver ( nameserver : SocketAddr ) -> Self {
69
+ fn with_nameserver ( nameserver : SocketAddr ) -> Self {
65
70
let mut config = hickory_resolver:: config:: ResolverConfig :: new ( ) ;
66
71
let nameserver_config = hickory_resolver:: config:: NameServerConfig :: new (
67
72
nameserver,
@@ -71,19 +76,115 @@ impl DnsResolver {
71
76
72
77
let builder =
73
78
TokioResolver :: builder_with_config ( config, TokioConnectionProvider :: default ( ) ) ;
74
- DnsResolver ( builder. build ( ) )
79
+ HickoryResolver ( builder. build ( ) )
80
+ }
81
+ }
82
+
83
+ impl Default for HickoryResolver {
84
+ fn default ( ) -> Self {
85
+ Self :: new ( )
75
86
}
87
+ }
76
88
77
- /// Removes all entries from the cache.
78
- pub fn clear_cache ( & self ) {
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 ) {
79
137
self . 0 . clear_cache ( ) ;
80
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
+ }
81
179
82
180
/// Lookup a TXT record.
83
- pub async fn lookup_txt ( & self , host : impl ToString , timeout : Duration ) -> Result < TxtLookup > {
84
- let host = host. to_string ( ) ;
85
- let res = time:: timeout ( timeout, self . 0 . txt_lookup ( host) ) . await ??;
86
- Ok ( TxtLookup ( res) )
181
+ pub async fn lookup_txt (
182
+ & self ,
183
+ host : impl ToString ,
184
+ timeout : Duration ,
185
+ ) -> Result < impl Iterator < Item = TxtRecord > > {
186
+ let res = time:: timeout ( timeout, self . 0 . lookup_txt ( host. to_string ( ) ) ) . await ??;
187
+ Ok ( res)
87
188
}
88
189
89
190
/// Perform an ipv4 lookup with a timeout.
@@ -92,9 +193,8 @@ impl DnsResolver {
92
193
host : impl ToString ,
93
194
timeout : Duration ,
94
195
) -> Result < impl Iterator < Item = IpAddr > > {
95
- let host = host. to_string ( ) ;
96
- let addrs = time:: timeout ( timeout, self . 0 . ipv4_lookup ( host) ) . await ??;
97
- Ok ( addrs. into_iter ( ) . map ( |ip| IpAddr :: V4 ( ip. 0 ) ) )
196
+ let addrs = time:: timeout ( timeout, self . 0 . lookup_ipv4 ( host. to_string ( ) ) ) . await ??;
197
+ Ok ( addrs. map ( |ip| IpAddr :: V4 ( ip) ) )
98
198
}
99
199
100
200
/// Perform an ipv6 lookup with a timeout.
@@ -103,9 +203,8 @@ impl DnsResolver {
103
203
host : impl ToString ,
104
204
timeout : Duration ,
105
205
) -> Result < impl Iterator < Item = IpAddr > > {
106
- let host = host. to_string ( ) ;
107
- let addrs = time:: timeout ( timeout, self . 0 . ipv6_lookup ( host) ) . await ??;
108
- Ok ( addrs. into_iter ( ) . map ( |ip| IpAddr :: V6 ( ip. 0 ) ) )
206
+ let addrs = time:: timeout ( timeout, self . 0 . lookup_ipv6 ( host. to_string ( ) ) ) . await ??;
207
+ Ok ( addrs. map ( |ip| IpAddr :: V6 ( ip) ) )
109
208
}
110
209
111
210
/// Resolve IPv4 and IPv6 in parallel with a timeout.
@@ -221,21 +320,19 @@ impl DnsResolver {
221
320
/// To lookup nodes that published their node info to the DNS servers run by n0,
222
321
/// pass [`N0_DNS_NODE_ORIGIN_PROD`] as `origin`.
223
322
pub async fn lookup_node_by_id ( & self , node_id : & NodeId , origin : & str ) -> Result < NodeInfo > {
224
- let attrs = crate :: node_info:: TxtAttrs :: < crate :: node_info:: IrohAttr > :: lookup_by_id (
225
- self , node_id, origin,
226
- )
227
- . await ?;
228
- let info = attrs. into ( ) ;
229
- Ok ( info)
323
+ let name = node_info:: node_domain ( node_id, origin) ;
324
+ let name = node_info:: ensure_iroh_txt_label ( name) ;
325
+ let lookup = self . lookup_txt ( name. clone ( ) , DNS_TIMEOUT ) . await ?;
326
+ let attrs = node_info:: TxtAttrs :: from_txt_lookup ( name, lookup) ?;
327
+ Ok ( attrs. into ( ) )
230
328
}
231
329
232
330
/// Looks up node info by DNS name.
233
331
pub async fn lookup_node_by_domain_name ( & self , name : & str ) -> Result < NodeInfo > {
234
- let attrs =
235
- crate :: node_info:: TxtAttrs :: < crate :: node_info:: IrohAttr > :: lookup_by_name ( self , name)
236
- . await ?;
237
- let info = attrs. into ( ) ;
238
- Ok ( info)
332
+ let name = node_info:: ensure_iroh_txt_label ( name. to_string ( ) ) ;
333
+ let lookup = self . lookup_txt ( name. clone ( ) , DNS_TIMEOUT ) . await ?;
334
+ let attrs = node_info:: TxtAttrs :: from_txt_lookup ( name, lookup) ?;
335
+ Ok ( attrs. into ( ) )
239
336
}
240
337
241
338
/// Looks up node info by DNS name in a staggered fashion.
@@ -270,52 +367,35 @@ impl DnsResolver {
270
367
}
271
368
}
272
369
273
- impl Default for DnsResolver {
274
- fn default ( ) -> Self {
275
- Self :: new ( )
276
- }
277
- }
278
-
279
- impl From < TokioResolver > for DnsResolver {
280
- fn from ( resolver : TokioResolver ) -> Self {
281
- DnsResolver ( resolver)
282
- }
283
- }
284
-
285
- /// TXT records returned from [`DnsResolver::lookup_txt`]
370
+ /// Record data for a TXT record.
286
371
#[ derive( Debug , Clone ) ]
287
- pub struct TxtLookup ( pub ( crate ) hickory_resolver :: lookup :: TxtLookup ) ;
372
+ pub struct TxtRecord ( Vec < Box < [ u8 ] > > ) ;
288
373
289
- impl From < hickory_resolver:: lookup:: TxtLookup > for TxtLookup {
290
- fn from ( value : hickory_resolver:: lookup:: TxtLookup ) -> Self {
291
- Self ( value)
374
+ impl TxtRecord {
375
+ ///
376
+ pub fn iter ( & self ) -> impl Iterator < Item = & [ u8 ] > {
377
+ self . 0 . iter ( ) . map ( |x| x. as_ref ( ) )
292
378
}
293
- }
294
-
295
- impl IntoIterator for TxtLookup {
296
- type Item = TXT ;
297
-
298
- type IntoIter = Box < dyn Iterator < Item = TXT > > ;
299
379
300
- fn into_iter ( self ) -> Self :: IntoIter {
301
- Box :: new ( self . 0 . into_iter ( ) . map ( TXT ) )
380
+ ///
381
+ pub fn to_strings ( & self ) -> impl Iterator < Item = String > + ' _ {
382
+ self . iter ( )
383
+ . map ( |cstr| String :: from_utf8_lossy ( & cstr) . to_string ( ) )
302
384
}
303
385
}
304
386
305
- /// Record data for a TXT record
306
- #[ derive( Debug , Clone ) ]
307
- pub struct TXT ( hickory_resolver:: proto:: rr:: rdata:: TXT ) ;
308
-
309
- impl TXT {
310
- /// Returns the raw character strings of this TXT record.
311
- pub fn txt_data ( & self ) -> & [ Box < [ u8 ] > ] {
312
- self . 0 . txt_data ( )
387
+ impl std:: fmt:: Display for TxtRecord {
388
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
389
+ for s in self . iter ( ) {
390
+ write ! ( f, "{}" , & String :: from_utf8_lossy( s) ) ?
391
+ }
392
+ Ok ( ( ) )
313
393
}
314
394
}
315
395
316
- impl fmt :: Display for TXT {
317
- fn fmt ( & self , f : & mut fmt :: Formatter < ' _ > ) -> fmt :: Result {
318
- write ! ( f , "{}" , self . 0 )
396
+ impl FromIterator < Box < [ u8 ] > > for TxtRecord {
397
+ fn from_iter < T : IntoIterator < Item = Box < [ u8 ] > > > ( iter : T ) -> Self {
398
+ Self ( iter . into_iter ( ) . collect ( ) )
319
399
}
320
400
}
321
401
0 commit comments