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