c_ares_resolver/resolver.rs
1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
2use std::sync::{Arc, Mutex};
3
4use crate::error::Error;
5use crate::eventloop::{EventLoop, EventLoopStopper};
6
7#[cfg(cares1_24)]
8use c_ares::AresString;
9
10#[cfg(cares1_29)]
11use c_ares::{ServerFailoverOptions, ServerStateFlags};
12
13/// Used to configure the behaviour of the resolver.
14#[derive(Default)]
15pub struct Options {
16 inner: c_ares::Options,
17}
18
19impl Options {
20 /// Returns a fresh `Options`, on which no values are set.
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 /// Set flags controlling the behaviour of the resolver.
26 pub fn set_flags(&mut self, flags: c_ares::Flags) -> &mut Self {
27 self.inner.set_flags(flags);
28 self
29 }
30
31 /// Set the number of milliseconds each name server is given to respond to a query on the first
32 /// try. (After the first try, the timeout algorithm becomes more complicated, but scales
33 /// linearly with the value of timeout). The default is 5000ms.
34 pub fn set_timeout(&mut self, ms: u32) -> &mut Self {
35 self.inner.set_timeout(ms);
36 self
37 }
38
39 /// Set the number of tries the resolver will try contacting each name server before giving up.
40 /// The default is four tries.
41 pub fn set_tries(&mut self, tries: u32) -> &mut Self {
42 self.inner.set_tries(tries);
43 self
44 }
45
46 /// Set the number of dots which must be present in a domain name for it to be queried for "as
47 /// is" prior to querying for it with the default domain extensions appended. The default
48 /// value is 1 unless set otherwise by resolv.conf or the RES_OPTIONS environment variable.
49 pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
50 self.inner.set_ndots(ndots);
51 self
52 }
53
54 /// Set the UDP port to use for queries. The default value is 53, the standard name service
55 /// port.
56 pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
57 self.inner.set_udp_port(udp_port);
58 self
59 }
60
61 /// Set the TCP port to use for queries. The default value is 53, the standard name service
62 /// port.
63 pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
64 self.inner.set_tcp_port(tcp_port);
65 self
66 }
67
68 /// Set the domains to search, instead of the domains specified in resolv.conf or the domain
69 /// derived from the kernel hostname variable.
70 pub fn set_domains(&mut self, domains: &[&str]) -> &mut Self {
71 self.inner.set_domains(domains);
72 self
73 }
74
75 /// Set the lookups to perform for host queries. `lookups` should be set to a string of the
76 /// characters "b" or "f", where "b" indicates a DNS lookup and "f" indicates a lookup in the
77 /// hosts file.
78 pub fn set_lookups(&mut self, lookups: &str) -> &mut Self {
79 self.inner.set_lookups(lookups);
80 self
81 }
82
83 /// Set the socket send buffer size.
84 pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
85 self.inner.set_sock_send_buffer_size(size);
86 self
87 }
88
89 /// Set the socket receive buffer size.
90 pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
91 self.inner.set_sock_receive_buffer_size(size);
92 self
93 }
94
95 /// Configure round robin selection of nameservers.
96 pub fn set_rotate(&mut self) -> &mut Self {
97 self.inner.set_rotate();
98 self
99 }
100
101 /// Prevent round robin selection of nameservers.
102 pub fn set_no_rotate(&mut self) -> &mut Self {
103 self.inner.set_no_rotate();
104 self
105 }
106
107 /// Set the EDNS packet size.
108 pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
109 self.inner.set_ednspsz(size);
110 self
111 }
112
113 /// Set the path to use for reading the resolv.conf file. The `resolvconf_path` should be set
114 /// to a path string, and will be honoured on *nix like systems. The default is
115 /// /etc/resolv.conf.
116 pub fn set_resolvconf_path(&mut self, resolvconf_path: &str) -> &mut Self {
117 self.inner.set_resolvconf_path(resolvconf_path);
118 self
119 }
120
121 /// Set the path to use for reading the hosts file. The `hosts_path` should be set to a path
122 /// string, and will be honoured on *nix like systems. The default is /etc/hosts.
123 #[cfg(cares1_19)]
124 pub fn set_hosts_path(&mut self, hosts_path: &str) -> &mut Self {
125 self.inner.set_hosts_path(hosts_path);
126 self
127 }
128
129 /// Set the maximum number of udp queries that can be sent on a single ephemeral port to a
130 /// given DNS server before a new ephemeral port is assigned. Any value of 0 or less will be
131 /// considered unlimited, and is the default.
132 #[cfg(cares1_20)]
133 pub fn set_udp_max_queries(&mut self, udp_max_queries: i32) -> &mut Self {
134 self.inner.set_udp_max_queries(udp_max_queries);
135 self
136 }
137
138 /// Set the upper bound for timeout between sequential retry attempts, in milliseconds. When
139 /// retrying queries, the timeout is increased from the requested timeout parameter, this caps
140 /// the value.
141 #[cfg(cares1_22)]
142 pub fn set_max_timeout(&mut self, max_timeout: i32) -> &mut Self {
143 self.inner.set_max_timeout(max_timeout);
144 self
145 }
146
147 /// Enable the built-in query cache. Will cache queries based on the returned TTL in the DNS
148 /// message. Only fully successful and NXDOMAIN query results will be cached.
149 ///
150 /// The provided value is the maximum number of seconds a query result may be cached; this will
151 /// override a larger TTL in the response message. This must be a non-zero value otherwise the
152 /// cache will be disabled.
153 #[cfg(cares1_23)]
154 pub fn set_query_cache_max_ttl(&mut self, qcache_max_ttl: u32) -> &mut Self {
155 self.inner.set_query_cache_max_ttl(qcache_max_ttl);
156 self
157 }
158
159 /// Set server failover options.
160 ///
161 /// When a DNS server fails to respond to a query, c-ares will deprioritize the server. On
162 /// subsequent queries, servers with fewer consecutive failures will be selected in preference.
163 /// However, in order to detect when such a server has recovered, c-ares will occasionally
164 /// retry failed servers. [`c_ares::ServerFailoverOptions`] contains options to control this
165 /// behaviour.
166 ///
167 /// If this option is not specified then c-ares will use a retry chance of 10% and a minimum
168 /// delay of 5 seconds.
169 #[cfg(cares1_29)]
170 pub fn set_server_failover_options(
171 &mut self,
172 server_failover_options: &ServerFailoverOptions,
173 ) -> &mut Self {
174 self.inner
175 .set_server_failover_options(server_failover_options);
176 self
177 }
178}
179
180/// An asynchronous DNS resolver, which returns results via callbacks.
181///
182/// Note that dropping the resolver will cause all outstanding requests to fail with result
183/// `c_ares::Error::EDESTRUCTION`.
184pub struct Resolver {
185 ares_channel: Arc<Mutex<c_ares::Channel>>,
186 _event_loop_stopper: EventLoopStopper,
187}
188
189impl Resolver {
190 /// Create a new `Resolver`, using default `Options`.
191 pub fn new() -> Result<Self, Error> {
192 let options = Options::default();
193 Self::with_options(options)
194 }
195
196 /// Create a new `Resolver`, with the given `Options`.
197 pub fn with_options(options: Options) -> Result<Self, Error> {
198 // Create and run the event loop.
199 let event_loop = EventLoop::new(options.inner)?;
200 let channel = Arc::clone(&event_loop.ares_channel);
201 let stopper = event_loop.run();
202
203 // Return the Resolver.
204 let resolver = Self {
205 ares_channel: channel,
206 _event_loop_stopper: stopper,
207 };
208 Ok(resolver)
209 }
210
211 /// Reinitialize a channel from system configuration.
212 #[cfg(cares1_22)]
213 pub fn reinit(&self) -> c_ares::Result<&Self> {
214 self.ares_channel.lock().unwrap().reinit()?;
215 Ok(self)
216 }
217
218 /// Set the list of servers to contact, instead of the servers specified in resolv.conf or the
219 /// local named.
220 ///
221 /// String format is `host[:port]`. IPv6 addresses with ports require square brackets eg
222 /// `[2001:4860:4860::8888]:53`.
223 pub fn set_servers(&self, servers: &[&str]) -> c_ares::Result<&Self> {
224 self.ares_channel.lock().unwrap().set_servers(servers)?;
225 Ok(self)
226 }
227
228 /// Retrieves the list of servers in comma delimited format.
229 #[cfg(cares1_24)]
230 pub fn get_servers(&self) -> AresString {
231 self.ares_channel.lock().unwrap().get_servers()
232 }
233
234 /// Set the local IPv4 address from which to make queries.
235 pub fn set_local_ipv4(&self, ipv4: Ipv4Addr) -> &Self {
236 self.ares_channel.lock().unwrap().set_local_ipv4(ipv4);
237 self
238 }
239
240 /// Set the local IPv6 address from which to make queries.
241 pub fn set_local_ipv6(&self, ipv6: &Ipv6Addr) -> &Self {
242 self.ares_channel.lock().unwrap().set_local_ipv6(ipv6);
243 self
244 }
245
246 /// Set the local device from which to make queries.
247 pub fn set_local_device(&self, device: &str) -> &Self {
248 self.ares_channel.lock().unwrap().set_local_device(device);
249 self
250 }
251
252 /// Initializes an address sortlist configuration, so that addresses returned by
253 /// `get_host_by_name()` are sorted according to the sortlist.
254 ///
255 /// Each element of the sortlist holds an IP-address/netmask pair. The netmask is optional but
256 /// follows the address after a slash if present. For example: "130.155.160.0/255.255.240.0",
257 /// or "130.155.0.0".
258 pub fn set_sortlist(&self, sortlist: &[&str]) -> c_ares::Result<&Self> {
259 self.ares_channel.lock().unwrap().set_sortlist(sortlist)?;
260 Ok(self)
261 }
262
263 /// Set a callback function to be invoked whenever a query on the channel completes.
264 ///
265 /// `callback(server, success, flags)` will be called when a query completes.
266 ///
267 /// - `server` indicates the DNS server that was used for the query.
268 /// - `success` indicates whether the query succeeded or not.
269 /// - `flags` is a bitmask of flags describing various aspects of the query.
270 #[cfg(cares1_29)]
271 pub fn set_server_state_callback<F>(&self, callback: F) -> &Self
272 where
273 F: FnMut(&str, bool, ServerStateFlags) + Send + 'static,
274 {
275 self.ares_channel
276 .lock()
277 .unwrap()
278 .set_server_state_callback(callback);
279 self
280 }
281
282 /// Look up the A records associated with `name`.
283 ///
284 /// On completion, `handler` is called with the result.
285 pub fn query_a<F>(&self, name: &str, handler: F)
286 where
287 F: FnOnce(c_ares::Result<c_ares::AResults>) + Send + 'static,
288 {
289 self.ares_channel.lock().unwrap().query_a(name, handler)
290 }
291
292 /// Search for the A records associated with `name`.
293 ///
294 /// On completion, `handler` is called with the result.
295 pub fn search_a<F>(&self, name: &str, handler: F)
296 where
297 F: FnOnce(c_ares::Result<c_ares::AResults>) + Send + 'static,
298 {
299 self.ares_channel.lock().unwrap().search_a(name, handler)
300 }
301
302 /// Look up the AAAA records associated with `name`.
303 ///
304 /// On completion, `handler` is called with the result.
305 pub fn query_aaaa<F>(&self, name: &str, handler: F)
306 where
307 F: FnOnce(c_ares::Result<c_ares::AAAAResults>) + Send + 'static,
308 {
309 self.ares_channel.lock().unwrap().query_aaaa(name, handler)
310 }
311
312 /// Search for the AAAA records associated with `name`.
313 ///
314 /// On completion, `handler` is called with the result.
315 pub fn search_aaaa<F>(&self, name: &str, handler: F)
316 where
317 F: FnOnce(c_ares::Result<c_ares::AAAAResults>) + Send + 'static,
318 {
319 self.ares_channel.lock().unwrap().search_aaaa(name, handler)
320 }
321
322 /// Look up the CAA records associated with `name`.
323 ///
324 /// On completion, `handler` is called with the result.
325 pub fn query_caa<F>(&self, name: &str, handler: F)
326 where
327 F: FnOnce(c_ares::Result<c_ares::CAAResults>) + Send + 'static,
328 {
329 self.ares_channel.lock().unwrap().query_caa(name, handler)
330 }
331
332 /// Search for the CAA records associated with `name`.
333 ///
334 /// On completion, `handler` is called with the result.
335 pub fn search_caa<F>(&self, name: &str, handler: F)
336 where
337 F: FnOnce(c_ares::Result<c_ares::CAAResults>) + Send + 'static,
338 {
339 self.ares_channel.lock().unwrap().search_caa(name, handler)
340 }
341
342 /// Look up the CNAME records associated with `name`.
343 ///
344 /// On completion, `handler` is called with the result.
345 pub fn query_cname<F>(&self, name: &str, handler: F)
346 where
347 F: FnOnce(c_ares::Result<c_ares::CNameResults>) + Send + 'static,
348 {
349 self.ares_channel.lock().unwrap().query_cname(name, handler)
350 }
351
352 /// Search for the CNAME records associated with `name`.
353 ///
354 /// On completion, `handler` is called with the result.
355 pub fn search_cname<F>(&self, name: &str, handler: F)
356 where
357 F: FnOnce(c_ares::Result<c_ares::CNameResults>) + Send + 'static,
358 {
359 self.ares_channel
360 .lock()
361 .unwrap()
362 .search_cname(name, handler)
363 }
364
365 /// Look up the MX records associated with `name`.
366 ///
367 /// On completion, `handler` is called with the result.
368 pub fn query_mx<F>(&self, name: &str, handler: F)
369 where
370 F: FnOnce(c_ares::Result<c_ares::MXResults>) + Send + 'static,
371 {
372 self.ares_channel.lock().unwrap().query_mx(name, handler)
373 }
374
375 /// Search for the MX records associated with `name`.
376 ///
377 /// On completion, `handler` is called with the result.
378 pub fn search_mx<F>(&self, name: &str, handler: F)
379 where
380 F: FnOnce(c_ares::Result<c_ares::MXResults>) + Send + 'static,
381 {
382 self.ares_channel.lock().unwrap().search_mx(name, handler)
383 }
384
385 /// Look up the NAPTR records associated with `name`.
386 ///
387 /// On completion, `handler` is called with the result.
388 pub fn query_naptr<F>(&self, name: &str, handler: F)
389 where
390 F: FnOnce(c_ares::Result<c_ares::NAPTRResults>) + Send + 'static,
391 {
392 self.ares_channel.lock().unwrap().query_naptr(name, handler)
393 }
394
395 /// Search for the NAPTR records associated with `name`.
396 ///
397 /// On completion, `handler` is called with the result.
398 pub fn search_naptr<F>(&self, name: &str, handler: F)
399 where
400 F: FnOnce(c_ares::Result<c_ares::NAPTRResults>) + Send + 'static,
401 {
402 self.ares_channel
403 .lock()
404 .unwrap()
405 .search_naptr(name, handler)
406 }
407
408 /// Look up the NS records associated with `name`.
409 ///
410 /// On completion, `handler` is called with the result.
411 pub fn query_ns<F>(&self, name: &str, handler: F)
412 where
413 F: FnOnce(c_ares::Result<c_ares::NSResults>) + Send + 'static,
414 {
415 self.ares_channel.lock().unwrap().query_ns(name, handler)
416 }
417
418 /// Search for the NS records associated with `name`.
419 ///
420 /// On completion, `handler` is called with the result.
421 pub fn search_ns<F>(&self, name: &str, handler: F)
422 where
423 F: FnOnce(c_ares::Result<c_ares::NSResults>) + Send + 'static,
424 {
425 self.ares_channel.lock().unwrap().search_ns(name, handler)
426 }
427
428 /// Look up the PTR records associated with `name`.
429 ///
430 /// On completion, `handler` is called with the result.
431 pub fn query_ptr<F>(&self, name: &str, handler: F)
432 where
433 F: FnOnce(c_ares::Result<c_ares::PTRResults>) + Send + 'static,
434 {
435 self.ares_channel.lock().unwrap().query_ptr(name, handler)
436 }
437
438 /// Search for the PTR records associated with `name`.
439 ///
440 /// On completion, `handler` is called with the result.
441 pub fn search_ptr<F>(&self, name: &str, handler: F)
442 where
443 F: FnOnce(c_ares::Result<c_ares::PTRResults>) + Send + 'static,
444 {
445 self.ares_channel.lock().unwrap().search_ptr(name, handler)
446 }
447
448 /// Look up the SOA record associated with `name`.
449 ///
450 /// On completion, `handler` is called with the result.
451 pub fn query_soa<F>(&self, name: &str, handler: F)
452 where
453 F: FnOnce(c_ares::Result<c_ares::SOAResult>) + Send + 'static,
454 {
455 self.ares_channel.lock().unwrap().query_soa(name, handler)
456 }
457
458 /// Search for the SOA record associated with `name`.
459 ///
460 /// On completion, `handler` is called with the result.
461 pub fn search_soa<F>(&self, name: &str, handler: F)
462 where
463 F: FnOnce(c_ares::Result<c_ares::SOAResult>) + Send + 'static,
464 {
465 self.ares_channel.lock().unwrap().search_soa(name, handler)
466 }
467
468 /// Look up the SRV records associated with `name`.
469 ///
470 /// On completion, `handler` is called with the result.
471 pub fn query_srv<F>(&self, name: &str, handler: F)
472 where
473 F: FnOnce(c_ares::Result<c_ares::SRVResults>) + Send + 'static,
474 {
475 self.ares_channel.lock().unwrap().query_srv(name, handler)
476 }
477
478 /// Search for the SRV records associated with `name`.
479 ///
480 /// On completion, `handler` is called with the result.
481 pub fn search_srv<F>(&self, name: &str, handler: F)
482 where
483 F: FnOnce(c_ares::Result<c_ares::SRVResults>) + Send + 'static,
484 {
485 self.ares_channel.lock().unwrap().search_srv(name, handler)
486 }
487
488 /// Look up the TXT records associated with `name`.
489 ///
490 /// On completion, `handler` is called with the result.
491 pub fn query_txt<F>(&self, name: &str, handler: F)
492 where
493 F: FnOnce(c_ares::Result<c_ares::TXTResults>) + Send + 'static,
494 {
495 self.ares_channel.lock().unwrap().query_txt(name, handler)
496 }
497
498 /// Search for the TXT records associated with `name`.
499 ///
500 /// On completion, `handler` is called with the result.
501 pub fn search_txt<F>(&self, name: &str, handler: F)
502 where
503 F: FnOnce(c_ares::Result<c_ares::TXTResults>) + Send + 'static,
504 {
505 self.ares_channel.lock().unwrap().search_txt(name, handler)
506 }
507
508 /// Look up the URI records associated with `name`.
509 ///
510 /// On completion, `handler` is called with the result.
511 pub fn query_uri<F>(&self, name: &str, handler: F)
512 where
513 F: FnOnce(c_ares::Result<c_ares::URIResults>) + Send + 'static,
514 {
515 self.ares_channel.lock().unwrap().query_uri(name, handler)
516 }
517
518 /// Search for the URI records associated with `name`.
519 ///
520 /// On completion, `handler` is called with the result.
521 pub fn search_uri<F>(&self, name: &str, handler: F)
522 where
523 F: FnOnce(c_ares::Result<c_ares::URIResults>) + Send + 'static,
524 {
525 self.ares_channel.lock().unwrap().search_uri(name, handler)
526 }
527
528 /// Perform a host query by address.
529 ///
530 /// On completion, `handler` is called with the result.
531 pub fn get_host_by_address<F>(&self, address: &IpAddr, handler: F)
532 where
533 F: FnOnce(c_ares::Result<c_ares::HostResults>) + Send + 'static,
534 {
535 self.ares_channel
536 .lock()
537 .unwrap()
538 .get_host_by_address(address, handler)
539 }
540
541 /// Perform a host query by name.
542 ///
543 /// On completion, `handler` is called with the result.
544 pub fn get_host_by_name<F>(&self, name: &str, family: c_ares::AddressFamily, handler: F)
545 where
546 F: FnOnce(c_ares::Result<c_ares::HostResults>) + Send + 'static,
547 {
548 self.ares_channel
549 .lock()
550 .unwrap()
551 .get_host_by_name(name, family, handler);
552 }
553
554 /// Address-to-nodename translation in protocol-independent manner.
555 ///
556 /// On completion, `handler` is called with the result.
557 pub fn get_name_info<F>(&self, address: &SocketAddr, flags: c_ares::NIFlags, handler: F)
558 where
559 F: FnOnce(c_ares::Result<c_ares::NameInfoResult>) + Send + 'static,
560 {
561 self.ares_channel
562 .lock()
563 .unwrap()
564 .get_name_info(address, flags, handler)
565 }
566
567 /// Initiate a single-question DNS query for `name`. The class and type of the query are per
568 /// the provided parameters, taking values as defined in `arpa/nameser.h`.
569 ///
570 /// On completion, `handler` is called with the result.
571 ///
572 /// This method is provided so that users can query DNS types for which `c-ares` does not
573 /// provide a parser; or in case a third-party parser is preferred. Usually, if a suitable
574 /// `query_xxx()` is available, that should be used.
575 pub fn query<F>(&self, name: &str, dns_class: u16, query_type: u16, handler: F)
576 where
577 F: FnOnce(c_ares::Result<&[u8]>) + Send + 'static,
578 {
579 self.ares_channel
580 .lock()
581 .unwrap()
582 .query(name, dns_class, query_type, handler);
583 }
584
585 /// Initiate a series of single-question DNS queries for `name`. The class and type of the
586 /// query are per the provided parameters, taking values as defined in `arpa/nameser.h`.
587 ///
588 /// On completion, `handler` is called with the result.
589 ///
590 /// This method is provided so that users can search DNS types for which `c-ares` does not
591 /// provide a parser; or in case a third-party parser is preferred. Usually, if a suitable
592 /// `search_xxx()` is available, that should be used.
593 pub fn search<F>(&self, name: &str, dns_class: u16, query_type: u16, handler: F)
594 where
595 F: FnOnce(c_ares::Result<&[u8]>) + Send + 'static,
596 {
597 self.ares_channel
598 .lock()
599 .unwrap()
600 .search(name, dns_class, query_type, handler);
601 }
602
603 /// Cancel all requests made on this `Resolver`.
604 pub fn cancel(&self) {
605 self.ares_channel.lock().unwrap().cancel();
606 }
607}