1
1
//! DNS resolver
2
2
3
3
use std:: {
4
- fmt:: { self , Write } ,
4
+ fmt,
5
5
future:: Future ,
6
6
net:: { IpAddr , Ipv6Addr , SocketAddr } ,
7
7
} ;
@@ -16,11 +16,33 @@ use url::Url;
16
16
17
17
pub mod node_info;
18
18
19
+ use node_info:: Error as NodeError ;
20
+
19
21
/// The n0 testing DNS node origin, for production.
20
22
pub const N0_DNS_NODE_ORIGIN_PROD : & str = "dns.iroh.link" ;
21
23
/// The n0 testing DNS node origin, for testing.
22
24
pub const N0_DNS_NODE_ORIGIN_STAGING : & str = "staging-dns.iroh.link" ;
23
25
26
+ /// Potential errors related to dns.
27
+ #[ allow( missing_docs) ]
28
+ #[ derive( Debug , thiserror:: Error ) ]
29
+ pub enum Error {
30
+ #[ error( transparent) ]
31
+ Timeout ( #[ from] tokio:: time:: error:: Elapsed ) ,
32
+ #[ error( "No response" ) ]
33
+ NoResponse ,
34
+ #[ error( "Resolve failed ipv4: {ipv4}, ipv6 {ipv6}" ) ]
35
+ ResolveBoth { ipv4 : Box < Error > , ipv6 : Box < Error > } ,
36
+ #[ error( "missing host" ) ]
37
+ MissingHost ,
38
+ #[ error( transparent) ]
39
+ Resolve ( #[ from] hickory_resolver:: ResolveError ) ,
40
+ #[ error( "invalid DNS response: not a query for _iroh.z32encodedpubkey" ) ]
41
+ InvalidResponse ,
42
+ #[ error( "no calls succeeded: [{}]" , errors. iter( ) . map( |e| e. to_string( ) ) . collect:: <Vec <_>>( ) . join( "" ) ) ]
43
+ Staggered { errors : Vec < Error > } ,
44
+ }
45
+
24
46
/// The DNS resolver used throughout `iroh`.
25
47
#[ derive( Debug , Clone ) ]
26
48
pub struct DnsResolver ( TokioResolver ) ;
@@ -74,7 +96,11 @@ impl DnsResolver {
74
96
}
75
97
76
98
/// Lookup a TXT record.
77
- pub async fn lookup_txt ( & self , host : impl ToString , timeout : Duration ) -> Result < TxtLookup > {
99
+ pub async fn lookup_txt (
100
+ & self ,
101
+ host : impl ToString ,
102
+ timeout : Duration ,
103
+ ) -> Result < TxtLookup , Error > {
78
104
let host = host. to_string ( ) ;
79
105
let res = time:: timeout ( timeout, self . 0 . txt_lookup ( host) ) . await ??;
80
106
Ok ( TxtLookup ( res) )
@@ -85,7 +111,7 @@ impl DnsResolver {
85
111
& self ,
86
112
host : impl ToString ,
87
113
timeout : Duration ,
88
- ) -> Result < impl Iterator < Item = IpAddr > > {
114
+ ) -> Result < impl Iterator < Item = IpAddr > , Error > {
89
115
let host = host. to_string ( ) ;
90
116
let addrs = time:: timeout ( timeout, self . 0 . ipv4_lookup ( host) ) . await ??;
91
117
Ok ( addrs. into_iter ( ) . map ( |ip| IpAddr :: V4 ( ip. 0 ) ) )
@@ -96,7 +122,7 @@ impl DnsResolver {
96
122
& self ,
97
123
host : impl ToString ,
98
124
timeout : Duration ,
99
- ) -> Result < impl Iterator < Item = IpAddr > > {
125
+ ) -> Result < impl Iterator < Item = IpAddr > , Error > {
100
126
let host = host. to_string ( ) ;
101
127
let addrs = time:: timeout ( timeout, self . 0 . ipv6_lookup ( host) ) . await ??;
102
128
Ok ( addrs. into_iter ( ) . map ( |ip| IpAddr :: V6 ( ip. 0 ) ) )
@@ -111,7 +137,7 @@ impl DnsResolver {
111
137
& self ,
112
138
host : impl ToString ,
113
139
timeout : Duration ,
114
- ) -> Result < impl Iterator < Item = IpAddr > > {
140
+ ) -> Result < impl Iterator < Item = IpAddr > , Error > {
115
141
let host = host. to_string ( ) ;
116
142
let res = tokio:: join!(
117
143
self . lookup_ipv4( host. clone( ) , timeout) ,
@@ -122,9 +148,10 @@ impl DnsResolver {
122
148
( Ok ( ipv4) , Ok ( ipv6) ) => Ok ( LookupIter :: Both ( ipv4. chain ( ipv6) ) ) ,
123
149
( Ok ( ipv4) , Err ( _) ) => Ok ( LookupIter :: Ipv4 ( ipv4) ) ,
124
150
( Err ( _) , Ok ( ipv6) ) => Ok ( LookupIter :: Ipv6 ( ipv6) ) ,
125
- ( Err ( ipv4_err) , Err ( ipv6_err) ) => {
126
- bail ! ( "Ipv4: {:?}, Ipv6: {:?}" , ipv4_err, ipv6_err)
127
- }
151
+ ( Err ( ipv4_err) , Err ( ipv6_err) ) => Err ( Error :: ResolveBoth {
152
+ ipv4 : Box :: new ( ipv4_err) ,
153
+ ipv6 : Box :: new ( ipv6_err) ,
154
+ } ) ,
128
155
}
129
156
}
130
157
@@ -134,8 +161,8 @@ impl DnsResolver {
134
161
url : & Url ,
135
162
prefer_ipv6 : bool ,
136
163
timeout : Duration ,
137
- ) -> Result < IpAddr > {
138
- let host = url. host ( ) . context ( "Invalid URL" ) ?;
164
+ ) -> Result < IpAddr , Error > {
165
+ let host = url. host ( ) . ok_or ( Error :: MissingHost ) ?;
139
166
match host {
140
167
url:: Host :: Domain ( domain) => {
141
168
// Need to do a DNS lookup
@@ -145,13 +172,20 @@ impl DnsResolver {
145
172
) ;
146
173
let ( v4, v6) = match lookup {
147
174
( Err ( ipv4_err) , Err ( ipv6_err) ) => {
148
- bail ! ( "Ipv4: {ipv4_err:?}, Ipv6: {ipv6_err:?}" ) ;
175
+ return Err ( Error :: ResolveBoth {
176
+ ipv4 : Box :: new ( ipv4_err) ,
177
+ ipv6 : Box :: new ( ipv6_err) ,
178
+ } ) ;
149
179
}
150
180
( Err ( _) , Ok ( mut v6) ) => ( None , v6. next ( ) ) ,
151
181
( Ok ( mut v4) , Err ( _) ) => ( v4. next ( ) , None ) ,
152
182
( Ok ( mut v4) , Ok ( mut v6) ) => ( v4. next ( ) , v6. next ( ) ) ,
153
183
} ;
154
- if prefer_ipv6 { v6. or ( v4) } else { v4. or ( v6) } . context ( "No response" )
184
+ if prefer_ipv6 {
185
+ v6. or ( v4) . ok_or ( Error :: NoResponse )
186
+ } else {
187
+ v4. or ( v6) . ok_or ( Error :: NoResponse )
188
+ }
155
189
}
156
190
url:: Host :: Ipv4 ( ip) => Ok ( IpAddr :: V4 ( ip) ) ,
157
191
url:: Host :: Ipv6 ( ip) => Ok ( IpAddr :: V6 ( ip) ) ,
@@ -169,10 +203,12 @@ impl DnsResolver {
169
203
host : impl ToString ,
170
204
timeout : Duration ,
171
205
delays_ms : & [ u64 ] ,
172
- ) -> Result < impl Iterator < Item = IpAddr > > {
206
+ ) -> Result < impl Iterator < Item = IpAddr > , Error > {
173
207
let host = host. to_string ( ) ;
174
208
let f = || self . lookup_ipv4 ( host. clone ( ) , timeout) ;
175
- stagger_call ( f, delays_ms) . await
209
+ stagger_call ( f, delays_ms)
210
+ . await
211
+ . map_err ( |errors| Error :: Staggered { errors } )
176
212
}
177
213
178
214
/// Perform an ipv6 lookup with a timeout in a staggered fashion.
@@ -186,10 +222,12 @@ impl DnsResolver {
186
222
host : impl ToString ,
187
223
timeout : Duration ,
188
224
delays_ms : & [ u64 ] ,
189
- ) -> Result < impl Iterator < Item = IpAddr > > {
225
+ ) -> Result < impl Iterator < Item = IpAddr > , Error > {
190
226
let host = host. to_string ( ) ;
191
227
let f = || self . lookup_ipv6 ( host. clone ( ) , timeout) ;
192
- stagger_call ( f, delays_ms) . await
228
+ stagger_call ( f, delays_ms)
229
+ . await
230
+ . map_err ( |errors| Error :: Staggered { errors } )
193
231
}
194
232
195
233
/// Race an ipv4 and ipv6 lookup with a timeout in a staggered fashion.
@@ -204,25 +242,31 @@ impl DnsResolver {
204
242
host : impl ToString ,
205
243
timeout : Duration ,
206
244
delays_ms : & [ u64 ] ,
207
- ) -> Result < impl Iterator < Item = IpAddr > > {
245
+ ) -> Result < impl Iterator < Item = IpAddr > , Error > {
208
246
let host = host. to_string ( ) ;
209
247
let f = || self . lookup_ipv4_ipv6 ( host. clone ( ) , timeout) ;
210
- stagger_call ( f, delays_ms) . await
248
+ stagger_call ( f, delays_ms)
249
+ . await
250
+ . map_err ( |errors| Error :: Staggered { errors } )
211
251
}
212
252
213
253
/// Looks up node info by [`NodeId`] and origin domain name.
214
254
///
215
255
/// To lookup nodes that published their node info to the DNS servers run by n0,
216
256
/// pass [`N0_DNS_NODE_ORIGIN_PROD`] as `origin`.
217
- pub async fn lookup_node_by_id ( & self , node_id : & NodeId , origin : & str ) -> Result < NodeAddr > {
257
+ pub async fn lookup_node_by_id (
258
+ & self ,
259
+ node_id : & NodeId ,
260
+ origin : & str ,
261
+ ) -> Result < NodeAddr , NodeError > {
218
262
let attrs =
219
263
node_info:: TxtAttrs :: < node_info:: IrohAttr > :: lookup_by_id ( self , node_id, origin) . await ?;
220
264
let info: node_info:: NodeInfo = attrs. into ( ) ;
221
265
Ok ( info. into ( ) )
222
266
}
223
267
224
268
/// Looks up node info by DNS name.
225
- pub async fn lookup_node_by_domain_name ( & self , name : & str ) -> Result < NodeAddr > {
269
+ pub async fn lookup_node_by_domain_name ( & self , name : & str ) -> Result < NodeAddr , NodeError > {
226
270
let attrs = node_info:: TxtAttrs :: < node_info:: IrohAttr > :: lookup_by_name ( self , name) . await ?;
227
271
let info: node_info:: NodeInfo = attrs. into ( ) ;
228
272
Ok ( info. into ( ) )
@@ -238,9 +282,11 @@ impl DnsResolver {
238
282
& self ,
239
283
name : & str ,
240
284
delays_ms : & [ u64 ] ,
241
- ) -> Result < NodeAddr > {
285
+ ) -> Result < NodeAddr , NodeError > {
242
286
let f = || self . lookup_node_by_domain_name ( name) ;
243
- stagger_call ( f, delays_ms) . await
287
+ stagger_call ( f, delays_ms)
288
+ . await
289
+ . map_err ( |errors| NodeError :: Staggered { errors } )
244
290
}
245
291
246
292
/// Looks up node info by [`NodeId`] and origin domain name.
@@ -254,9 +300,11 @@ impl DnsResolver {
254
300
node_id : & NodeId ,
255
301
origin : & str ,
256
302
delays_ms : & [ u64 ] ,
257
- ) -> Result < NodeAddr > {
303
+ ) -> Result < NodeAddr , NodeError > {
258
304
let f = || self . lookup_node_by_id ( node_id, origin) ;
259
- stagger_call ( f, delays_ms) . await
305
+ stagger_call ( f, delays_ms)
306
+ . await
307
+ . map_err ( |errors| NodeError :: Staggered { errors } )
260
308
}
261
309
}
262
310
@@ -346,10 +394,10 @@ impl<A: Iterator<Item = IpAddr>, B: Iterator<Item = IpAddr>> Iterator for Lookup
346
394
///
347
395
/// The first call is performed immediately. The first call to succeed generates an Ok result
348
396
/// ignoring any previous error. If all calls fail, an error summarizing all errors is returned.
349
- async fn stagger_call < T , F : Fn ( ) -> Fut , Fut : Future < Output = Result < T > > > (
397
+ async fn stagger_call < T , E , F : Fn ( ) -> Fut , Fut : Future < Output = Result < T , E > > > (
350
398
f : F ,
351
399
delays_ms : & [ u64 ] ,
352
- ) -> Result < T > {
400
+ ) -> Result < T , Vec < E > > {
353
401
let mut calls = n0_future:: FuturesUnorderedBounded :: new ( delays_ms. len ( ) + 1 ) ;
354
402
// NOTE: we add the 0 delay here to have a uniform set of futures. This is more performant than
355
403
// using alternatives that allow futures of different types.
@@ -371,13 +419,7 @@ async fn stagger_call<T, F: Fn() -> Fut, Fut: Future<Output = Result<T>>>(
371
419
}
372
420
}
373
421
374
- bail ! (
375
- "no calls succeed: [ {}]" ,
376
- errors. into_iter( ) . fold( String :: new( ) , |mut summary, e| {
377
- write!( summary, "{e} " ) . expect( "infallible" ) ;
378
- summary
379
- } )
380
- )
422
+ Err ( errors)
381
423
}
382
424
383
425
#[ cfg( test) ]
@@ -397,7 +439,7 @@ pub(crate) mod tests {
397
439
let r_pos = DONE_CALL . fetch_add ( 1 , std:: sync:: atomic:: Ordering :: Relaxed ) ;
398
440
async move {
399
441
tracing:: info!( r_pos, "call" ) ;
400
- CALL_RESULTS [ r_pos] . map_err ( |e| anyhow :: anyhow! ( "{e}" ) )
442
+ CALL_RESULTS [ r_pos] . map_err ( |_| Error :: InvalidResponse )
401
443
}
402
444
} ;
403
445
0 commit comments