c_ares/channel/
options.rs1use core::ffi::c_int;
2use std::ffi::CString;
3use std::fmt;
4use std::mem;
5use std::sync::Arc;
6use std::time::Duration;
7
8use crate::Flags;
9use crate::error::{Error, Result};
10#[cfg(cares1_26)]
11use crate::types::EventSys;
12use crate::types::Socket;
13
14pub(crate) type SocketStateCallback = dyn Fn(Socket, bool, bool) + Send + Sync + 'static;
15
16#[cfg(cares1_29)]
23#[derive(Debug)]
24pub struct ServerFailoverOptions {
25 retry_chance: u16,
26 retry_delay: Duration,
27}
28
29#[cfg(cares1_29)]
30impl Default for ServerFailoverOptions {
31 fn default() -> Self {
32 Self {
33 retry_chance: 10,
34 retry_delay: Duration::from_secs(5),
35 }
36 }
37}
38
39#[cfg(cares1_29)]
40impl ServerFailoverOptions {
41 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn set_retry_chance(&mut self, retry_chance: u16) -> &mut Self {
49 self.retry_chance = retry_chance;
50 self
51 }
52
53 pub fn set_retry_delay(&mut self, retry_delay: Duration) -> &mut Self {
56 self.retry_delay = retry_delay;
57 self
58 }
59}
60
61pub struct Options {
63 #[allow(clippy::struct_field_names)]
66 pub(super) ares_options: c_ares_sys::ares_options,
67 pub(super) optmask: c_int,
68 pub(super) domains: Vec<CString>,
69 pub(super) lookups: Option<CString>,
70 pub(super) resolvconf_path: Option<CString>,
71 #[cfg(cares1_19)]
72 pub(super) hosts_path: Option<CString>,
73 pub(super) socket_state_callback: Option<Arc<SocketStateCallback>>,
74}
75
76impl fmt::Debug for Options {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.debug_struct("Options").finish_non_exhaustive()
79 }
80}
81
82impl Default for Options {
83 fn default() -> Self {
84 Self {
85 ares_options: unsafe { mem::MaybeUninit::zeroed().assume_init() },
86 optmask: 0,
87 domains: vec![],
88 lookups: None,
89 resolvconf_path: None,
90 #[cfg(cares1_19)]
91 hosts_path: None,
92 socket_state_callback: None,
93 }
94 }
95}
96
97impl Options {
98 pub fn new() -> Self {
112 Self::default()
113 }
114
115 pub fn set_flags(&mut self, flags: Flags) -> &mut Self {
118 self.ares_options.flags = flags.bits();
119 self.optmask |= c_ares_sys::ARES_OPT_FLAGS;
120 self
121 }
122
123 pub fn set_timeout(&mut self, timeout: Duration) -> &mut Self {
127 self.ares_options.timeout = c_int::try_from(timeout.as_millis()).unwrap_or(c_int::MAX);
128 self.optmask |= c_ares_sys::ARES_OPT_TIMEOUTMS;
129 self
130 }
131
132 pub fn set_tries(&mut self, tries: u32) -> &mut Self {
135 self.ares_options.tries = c_int::try_from(tries).unwrap_or(c_int::MAX);
136 self.optmask |= c_ares_sys::ARES_OPT_TRIES;
137 self
138 }
139
140 pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
144 self.ares_options.ndots = c_int::try_from(ndots).unwrap_or(c_int::MAX);
145 self.optmask |= c_ares_sys::ARES_OPT_NDOTS;
146 self
147 }
148
149 pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
152 self.ares_options.udp_port = udp_port;
153 self.optmask |= c_ares_sys::ARES_OPT_UDP_PORT;
154 self
155 }
156
157 pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
160 self.ares_options.tcp_port = tcp_port;
161 self.optmask |= c_ares_sys::ARES_OPT_TCP_PORT;
162 self
163 }
164
165 pub fn set_domains<I, S>(&mut self, domains: I) -> Result<&mut Self>
168 where
169 I: IntoIterator<Item = S>,
170 S: AsRef<str>,
171 {
172 self.domains = domains
173 .into_iter()
174 .map(|s| CString::new(s.as_ref()).map_err(|_| Error::EBADSTR))
175 .collect::<Result<Vec<_>>>()?;
176 self.optmask |= c_ares_sys::ARES_OPT_DOMAINS;
177 Ok(self)
178 }
179
180 pub fn set_lookups(&mut self, lookups: &str) -> Result<&mut Self> {
184 let c_lookups = CString::new(lookups).map_err(|_| Error::EBADSTR)?;
185 self.lookups = Some(c_lookups);
186 self.optmask |= c_ares_sys::ARES_OPT_LOOKUPS;
187 Ok(self)
188 }
189
190 pub fn set_socket_state_callback<F>(&mut self, callback: F) -> &mut Self
197 where
198 F: Fn(Socket, bool, bool) + Send + Sync + 'static,
199 {
200 let boxed_callback = Arc::new(callback);
201 self.ares_options.sock_state_cb = Some(super::socket_state_callback::<F>);
202 self.ares_options.sock_state_cb_data = Arc::as_ptr(&boxed_callback).cast_mut().cast();
203 self.socket_state_callback = Some(boxed_callback);
204 self.optmask |= c_ares_sys::ARES_OPT_SOCK_STATE_CB;
205 self
206 }
207
208 pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
210 self.ares_options.socket_send_buffer_size = c_int::try_from(size).unwrap_or(c_int::MAX);
211 self.optmask |= c_ares_sys::ARES_OPT_SOCK_SNDBUF;
212 self
213 }
214
215 pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
217 self.ares_options.socket_receive_buffer_size = c_int::try_from(size).unwrap_or(c_int::MAX);
218 self.optmask |= c_ares_sys::ARES_OPT_SOCK_RCVBUF;
219 self
220 }
221
222 pub fn set_rotate(&mut self) -> &mut Self {
224 self.optmask &= !c_ares_sys::ARES_OPT_NOROTATE;
225 self.optmask |= c_ares_sys::ARES_OPT_ROTATE;
226 self
227 }
228
229 pub fn set_no_rotate(&mut self) -> &mut Self {
231 self.optmask &= !c_ares_sys::ARES_OPT_ROTATE;
232 self.optmask |= c_ares_sys::ARES_OPT_NOROTATE;
233 self
234 }
235
236 pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
238 self.ares_options.ednspsz = c_int::try_from(size).unwrap_or(c_int::MAX);
239 self.optmask |= c_ares_sys::ARES_OPT_EDNSPSZ;
240 self
241 }
242
243 pub fn set_resolvconf_path(&mut self, resolvconf_path: &str) -> Result<&mut Self> {
247 let c_resolvconf_path = CString::new(resolvconf_path).map_err(|_| Error::EBADSTR)?;
248 self.resolvconf_path = Some(c_resolvconf_path);
249 self.optmask |= c_ares_sys::ARES_OPT_RESOLVCONF;
250 Ok(self)
251 }
252
253 #[cfg(cares1_19)]
256 pub fn set_hosts_path(&mut self, hosts_path: &str) -> Result<&mut Self> {
257 let c_hosts_path = CString::new(hosts_path).map_err(|_| Error::EBADSTR)?;
258 self.hosts_path = Some(c_hosts_path);
259 self.optmask |= c_ares_sys::ARES_OPT_HOSTS_FILE;
260 Ok(self)
261 }
262
263 #[cfg(cares1_20)]
268 pub fn set_udp_max_queries(&mut self, udp_max_queries: Option<u32>) -> &mut Self {
269 self.ares_options.udp_max_queries = match udp_max_queries {
270 None => 0,
271 Some(n) => c_int::try_from(n).unwrap_or(c_int::MAX),
272 };
273 self.optmask |= c_ares_sys::ARES_OPT_UDP_MAX_QUERIES;
274 self
275 }
276
277 #[cfg(cares1_22)]
280 pub fn set_max_timeout(&mut self, max_timeout: Duration) -> &mut Self {
281 self.ares_options.maxtimeout =
282 c_int::try_from(max_timeout.as_millis()).unwrap_or(c_int::MAX);
283 self.optmask |= c_ares_sys::ARES_OPT_MAXTIMEOUTMS;
284 self
285 }
286
287 #[cfg(cares1_23)]
294 pub fn set_query_cache_max_ttl(&mut self, qcache_max_ttl: u32) -> &mut Self {
295 self.ares_options.qcache_max_ttl = qcache_max_ttl;
296 self.optmask |= c_ares_sys::ARES_OPT_QUERY_CACHE;
297 self
298 }
299
300 #[cfg(cares1_29)]
305 pub fn set_server_failover_options(
306 &mut self,
307 server_failover_options: &ServerFailoverOptions,
308 ) -> &mut Self {
309 self.ares_options.server_failover_opts.retry_chance = server_failover_options.retry_chance;
310 self.ares_options.server_failover_opts.retry_delay =
311 server_failover_options.retry_delay.as_millis() as usize;
312 self.optmask |= c_ares_sys::ARES_OPT_SERVER_FAILOVER;
313 self
314 }
315
316 #[cfg(cares1_26)]
325 pub fn set_event_thread(&mut self, evsys: EventSys) -> &mut Self {
326 self.ares_options.evsys = evsys.into();
327 self.optmask |= c_ares_sys::ARES_OPT_EVENT_THREAD;
328 self
329 }
330}
331
332unsafe impl Send for Options {}
333unsafe impl Sync for Options {}
334
335#[cfg(test)]
336mod tests {
337 use super::super::Channel;
338 use super::*;
339
340 #[test]
341 fn options_default() {
342 let _options = Options::new();
343 }
344
345 #[test]
346 fn options_set_flags() {
347 let mut options = Options::new();
348 options.set_flags(Flags::USEVC | Flags::STAYOPEN);
349 }
350
351 #[test]
352 fn options_set_timeout() {
353 let mut options = Options::new();
354 options.set_timeout(Duration::from_secs(5));
355 }
356
357 #[test]
358 fn options_set_tries() {
359 let mut options = Options::new();
360 options.set_tries(5);
361 }
362
363 #[test]
364 fn options_set_ndots() {
365 let mut options = Options::new();
366 options.set_ndots(2);
367 }
368
369 #[test]
370 fn options_set_ports() {
371 let mut options = Options::new();
372 options.set_udp_port(53);
373 options.set_tcp_port(53);
374 }
375
376 #[test]
377 fn options_set_domains() {
378 let mut options = Options::new();
379 options.set_domains(["example.com", "test.local"]).unwrap();
380 }
381
382 #[test]
383 fn options_set_domains_owned_strings() {
384 let mut options = Options::new();
385 let domains: Vec<String> = vec!["example.com".into(), "test.local".into()];
386 options.set_domains(domains).unwrap();
387 }
388
389 #[test]
390 fn options_set_lookups() {
391 let mut options = Options::new();
392 options.set_lookups("bf").unwrap();
393 }
394
395 #[test]
396 fn options_set_socket_state_callback() {
397 let mut options = Options::new();
398 options.set_socket_state_callback(|_socket, _read, _write| {});
399 }
400
401 #[test]
402 fn options_set_sock_buffer_sizes() {
403 let mut options = Options::new();
404 options.set_sock_send_buffer_size(65536);
405 options.set_sock_receive_buffer_size(65536);
406 }
407
408 #[test]
409 fn options_set_rotate() {
410 let mut options = Options::new();
411 options.set_rotate();
412 }
413
414 #[test]
415 fn options_set_no_rotate() {
416 let mut options = Options::new();
417 options.set_no_rotate();
418 }
419
420 #[test]
421 fn options_set_ednspsz() {
422 let mut options = Options::new();
423 options.set_ednspsz(4096);
424 }
425
426 #[test]
427 fn options_set_resolvconf_path() {
428 let mut options = Options::new();
429 options.set_resolvconf_path("/etc/resolv.conf").unwrap();
430 }
431
432 #[cfg(cares1_19)]
433 #[test]
434 fn options_set_hosts_path() {
435 let mut options = Options::new();
436 options.set_hosts_path("/etc/hosts").unwrap();
437 }
438
439 #[cfg(cares1_20)]
440 #[test]
441 fn options_set_udp_max_queries() {
442 let mut options = Options::new();
443 options.set_udp_max_queries(Some(100));
444 }
445
446 #[cfg(cares1_20)]
447 #[test]
448 fn options_set_udp_max_queries_none() {
449 let mut options = Options::new();
450 options.set_udp_max_queries(None);
451 }
452
453 #[cfg(cares1_22)]
454 #[test]
455 fn options_set_max_timeout() {
456 let mut options = Options::new();
457 options.set_max_timeout(Duration::from_secs(30));
458 }
459
460 #[cfg(cares1_23)]
461 #[test]
462 fn options_set_query_cache_max_ttl() {
463 let mut options = Options::new();
464 options.set_query_cache_max_ttl(3600);
465 }
466
467 #[cfg(cares1_29)]
468 #[test]
469 fn options_set_server_failover_options() {
470 let mut options = Options::new();
471 let mut failover_opts = ServerFailoverOptions::new();
472 failover_opts
473 .set_retry_chance(5)
474 .set_retry_delay(Duration::from_secs(10));
475 options.set_server_failover_options(&failover_opts);
476 }
477
478 #[test]
479 fn options_builder_chain() {
480 let mut options = Options::new();
481 options
482 .set_flags(Flags::USEVC)
483 .set_timeout(Duration::from_secs(3))
484 .set_tries(2)
485 .set_ndots(1)
486 .set_udp_port(53)
487 .set_tcp_port(53)
488 .set_lookups("b")
489 .unwrap();
490 }
491
492 #[test]
493 fn options_full_builder_chain() {
494 let mut options = Options::new();
495 options
496 .set_flags(Flags::USEVC | Flags::STAYOPEN)
497 .set_timeout(Duration::from_secs(2))
498 .set_tries(3)
499 .set_ndots(2)
500 .set_udp_port(53)
501 .set_tcp_port(53)
502 .set_domains(["example.com"])
503 .unwrap()
504 .set_lookups("b")
505 .unwrap()
506 .set_sock_send_buffer_size(32768)
507 .set_sock_receive_buffer_size(32768)
508 .set_rotate()
509 .set_ednspsz(4096)
510 .set_resolvconf_path("/etc/resolv.conf")
511 .unwrap();
512 let channel = Channel::with_options(options);
513 assert!(channel.is_ok());
514 }
515
516 #[test]
517 fn options_is_send() {
518 fn assert_send<T: Send>() {}
519 assert_send::<Options>();
520 }
521
522 #[test]
523 fn options_is_sync() {
524 fn assert_sync<T: Sync>() {}
525 assert_sync::<Options>();
526 }
527
528 #[test]
529 fn options_with_socket_callback_creates_channel() {
530 let mut options = Options::new();
531 options.set_socket_state_callback(|_socket, _read, _write| {
532 });
534 let channel = Channel::with_options(options);
535 assert!(channel.is_ok());
536 }
537
538 #[cfg(cares1_19)]
539 #[test]
540 fn options_hosts_path_creates_channel() {
541 let mut options = Options::new();
542 options.set_hosts_path("/etc/hosts").unwrap();
543 let channel = Channel::with_options(options);
544 assert!(channel.is_ok());
545 }
546
547 #[test]
548 fn debug_options() {
549 let options = Options::new();
550 let debug = format!("{options:?}");
551 assert!(debug.contains("Options"));
552 }
553
554 #[test]
555 #[cfg(cares1_29)]
556 fn debug_server_failover_options() {
557 let opts = ServerFailoverOptions::new();
558 let debug = format!("{opts:?}");
559 assert!(debug.contains("ServerFailoverOptions"));
560 }
561
562 #[cfg(cares1_26)]
563 #[test]
564 fn options_set_event_thread() {
565 let mut options = Options::new();
566 options.set_event_thread(EventSys::Default);
567 }
568
569 #[cfg(cares1_26)]
570 #[test]
571 fn options_set_event_thread_creates_channel() {
572 if !crate::thread_safety() {
574 return;
575 }
576 let mut options = Options::new();
577 options.set_event_thread(EventSys::Default);
578 let channel = Channel::with_options(options);
579 assert!(channel.is_ok());
580 }
581}