1use std::path::Path;
21use std::sync::Arc;
22use std::time::Duration;
23
24use futures::future::join_all;
25use futures::stream::{self, StreamExt};
26use futures::Future;
27use rand::seq::IndexedRandom;
28use tokio::task;
29use tracing::{debug, instrument};
30
31pub use builder::ResolverGroupBuilder;
32pub use error::Error;
33pub use lookup::{Lookup, Lookups};
34pub use query::{MultiQuery, UniQuery};
35
36use crate::nameserver::{NameServerConfig, NameServerConfigGroup};
37use crate::system_config;
38use crate::Result;
39use std::fmt::Display;
40use std::fmt::Formatter;
41use std::str::FromStr;
42
43pub mod builder;
44pub mod delegation;
45pub mod error;
46pub mod lookup;
47pub mod predefined;
48pub mod query;
49pub mod raw;
50
51pub type ResolverResult<T> = std::result::Result<T, Error>;
52
53#[derive(Debug)]
55pub struct ResolverConfig {
56 name_server_config: NameServerConfig,
57}
58
59impl ResolverConfig {
60 pub fn new(name_server_config: NameServerConfig) -> Self {
61 ResolverConfig { name_server_config }
62 }
63}
64
65impl From<NameServerConfig> for ResolverConfig {
66 fn from(ns_config: NameServerConfig) -> Self {
67 ResolverConfig {
68 name_server_config: ns_config,
69 }
70 }
71}
72
73#[derive(Debug, Clone)]
78pub struct ResolverOpts {
79 pub retries: usize,
80 pub max_concurrent_requests: usize,
82 pub ndots: usize,
83 pub preserve_intermediates: bool,
84 pub expects_multiple_responses: bool,
86 pub timeout: Duration,
87 pub abort_on_error: bool,
88 pub abort_on_timeout: bool,
89}
90
91impl ResolverOpts {
92 pub fn from_system_config() -> Result<ResolverOpts> {
96 let opts = system_config::load_from_system_config()?;
97 Ok(opts)
98 }
99
100 pub fn from_system_config_path<P: AsRef<Path>>(path: P) -> Result<ResolverOpts> {
101 let opts = system_config::load_from_system_config_path(path)?;
102 Ok(opts)
103 }
104}
105
106impl Default for ResolverOpts {
107 fn default() -> Self {
108 ResolverOpts {
109 retries: 1,
110 max_concurrent_requests: 5,
111 ndots: 1,
112 preserve_intermediates: false,
113 expects_multiple_responses: false,
114 timeout: Duration::from_secs(5),
115 abort_on_error: true,
116 abort_on_timeout: true,
117 }
118 }
119}
120
121#[derive(Debug)]
123pub struct ResolverConfigGroup {
124 configs: Vec<ResolverConfig>,
125}
126
127impl ResolverConfigGroup {
128 pub fn new(resolver_configs: Vec<ResolverConfig>) -> ResolverConfigGroup {
129 ResolverConfigGroup {
130 configs: resolver_configs,
131 }
132 }
133
134 pub fn merge(&mut self, other: Self) {
136 self.configs.extend(other.configs)
137 }
138}
139
140impl From<NameServerConfigGroup> for ResolverConfigGroup {
141 fn from(configs: NameServerConfigGroup) -> Self {
142 let resolver_confings: Vec<_> = configs.into_iter().map(From::from).collect();
143 ResolverConfigGroup::new(resolver_confings)
144 }
145}
146
147impl IntoIterator for ResolverConfigGroup {
148 type Item = ResolverConfig;
149 type IntoIter = std::vec::IntoIter<Self::Item>;
150
151 fn into_iter(self) -> Self::IntoIter {
152 self.configs.into_iter()
153 }
154}
155
156#[derive(Debug, Clone)]
161pub struct Resolver {
162 pub(crate) inner: Arc<hickory_resolver::TokioResolver>,
163 pub(crate) opts: Arc<ResolverOpts>,
164 pub(crate) name_server: Arc<NameServerConfig>,
165}
166
167impl Resolver {
168 #[instrument(name = "create resolver", level = "info", skip(config, opts), fields(server = %config.name_server_config))]
172 pub async fn new(config: ResolverConfig, opts: ResolverOpts) -> ResolverResult<Self> {
173 let name_server = config.name_server_config.clone();
174 let tr_opts = opts.clone().into();
175 let tr_config: hickory_resolver::config::ResolverConfig = config.into();
176 let tr_resolver = hickory_resolver::Resolver::builder_with_config(
177 tr_config,
178 hickory_resolver::name_server::TokioConnectionProvider::default(),
179 )
180 .with_options(tr_opts)
181 .build();
182
183 Ok(Resolver {
184 inner: Arc::new(tr_resolver),
185 opts: Arc::new(opts),
186 name_server: Arc::new(name_server),
187 })
188 }
189
190 pub async fn lookup<T: Into<MultiQuery>>(&self, query: T) -> ResolverResult<Lookups> {
191 lookup::lookup(self.clone(), query).await
192 }
193
194 pub fn name(&self) -> String {
195 self.name_server.to_string()
196 }
197}
198
199#[derive(Debug, Clone)]
201pub struct ResolverGroupOpts {
202 pub max_concurrent: usize,
204 pub limit: Option<usize>,
206 pub mode: Mode,
209}
210
211#[derive(Debug, Eq, PartialEq, Clone, Copy)]
213pub enum Mode {
214 Multi,
216 Uni,
218}
219
220impl Display for Mode {
221 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
222 match self {
223 Mode::Multi => write!(f, "multi"),
224 Mode::Uni => write!(f, "uni"),
225 }
226 }
227}
228
229impl FromStr for Mode {
230 type Err = crate::error::Error;
231
232 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
233 match s {
234 "multi" => Ok(Mode::Multi),
235 "uni" => Ok(Mode::Uni),
236 _ => Err(crate::error::Error::ParserError {
237 what: s.to_string(),
238 to: "Mode",
239 why: "no such mode".to_string(),
240 }),
241 }
242 }
243}
244
245impl Default for ResolverGroupOpts {
246 fn default() -> Self {
247 ResolverGroupOpts {
248 max_concurrent: 10,
249 limit: None,
250 mode: Mode::Multi,
251 }
252 }
253}
254
255#[derive(Debug)]
262pub struct ResolverGroup {
263 pub(crate) resolvers: Vec<Resolver>,
264 pub(crate) opts: ResolverGroupOpts,
265}
266
267impl ResolverGroup {
268 pub fn new<T: Into<Vec<Resolver>>>(resolvers: T, opts: ResolverGroupOpts) -> Self {
269 ResolverGroup {
270 resolvers: resolvers.into(),
271 opts,
272 }
273 }
274
275 pub async fn from_configs<T: IntoIterator<Item = ResolverConfig>>(
276 configs: T,
277 resolver_opts: ResolverOpts,
278 opts: ResolverGroupOpts,
279 ) -> Result<Self> {
280 let futures: Vec<_> = configs
282 .into_iter()
283 .map(|config| Resolver::new(config, resolver_opts.clone()))
284 .collect();
285
286 let resolvers: ResolverResult<Vec<_>> = join_all(futures).await.drain(..).collect();
288
289 let resolvers = resolvers?;
291
292 Ok(Self::new(resolvers, opts))
293 }
294
295 pub async fn from_system_config(opts: ResolverGroupOpts) -> Result<Self> {
296 let resolver_opts = ResolverOpts::from_system_config()?;
297 let configs: ResolverConfigGroup = NameServerConfigGroup::from_system_config()?.into();
298 ResolverGroup::from_configs(configs, resolver_opts, opts).await
299 }
300
301 pub async fn lookup<T: Into<MultiQuery>>(&self, query: T) -> ResolverResult<Lookups> {
302 match self.opts.mode {
303 Mode::Multi => self.multi_lookup(query).await,
304 Mode::Uni => self.uni_lookup(query).await,
305 }
306 }
307
308 async fn multi_lookup<T: Into<MultiQuery>>(&self, query: T) -> ResolverResult<Lookups> {
309 let multi_query = query.into();
310 let mut resolvers = self.resolvers.clone();
311
312 let lookup_futures: Vec<_> = resolvers
313 .drain(..)
314 .take(self.opts.limit.unwrap_or(self.resolvers.len()))
315 .map(|resolver| lookup::lookup(resolver, multi_query.clone()))
316 .collect();
317 let lookups = sliding_window_lookups(lookup_futures, self.opts.max_concurrent);
318 let lookups = task::spawn(lookups).await?;
319
320 Ok(lookups)
321 }
322
323 async fn uni_lookup<T: Into<MultiQuery>>(&self, query: T) -> ResolverResult<Lookups> {
324 if self.resolvers.is_empty() {
325 return Err(Error::ResolveError {
326 reason: "no resolvers available".to_string(),
327 });
328 }
329
330 let mut rng = rand::rng();
331 let multi_query = query.into();
332 let resolvers = self.resolvers.as_slice();
333
334 let lookup_futures: Vec<_> = multi_query
335 .into_uni_queries()
336 .drain(..)
337 .map(|q| {
338 let resolver = resolvers.choose(&mut rng).expect("resolvers is non-empty");
339 lookup::lookup(resolver.clone(), q)
340 })
341 .collect();
342 let lookups = sliding_window_lookups(lookup_futures, self.opts.max_concurrent);
343 let lookups = task::spawn(lookups).await?;
344
345 Ok(lookups)
346 }
347
348 pub fn merge(&mut self, other: Self) {
352 self.resolvers.extend(other.resolvers)
353 }
354
355 pub fn add(&mut self, resolver: Resolver) {
356 self.resolvers.push(resolver)
357 }
358
359 pub fn set_opts(&mut self, opts: ResolverGroupOpts) {
360 self.opts = opts
361 }
362
363 pub fn opts(&self) -> &ResolverGroupOpts {
364 &self.opts
365 }
366
367 pub fn resolvers(&self) -> &[Resolver] {
368 self.resolvers.as_slice()
369 }
370
371 pub fn len(&self) -> usize {
372 self.resolvers.len()
373 }
374
375 pub fn is_empty(&self) -> bool {
376 self.resolvers.is_empty()
377 }
378}
379
380async fn sliding_window_lookups(
381 futures: Vec<impl Future<Output = ResolverResult<Lookups>>>,
382 max_concurrent: usize,
383) -> Lookups {
384 let results: Vec<_> = stream::iter(futures)
385 .buffer_unordered(max_concurrent)
386 .collect::<Vec<_>>()
387 .await;
388
389 let mut error_count = 0usize;
390 let lookups: Vec<Lookup> = results
391 .into_iter()
392 .filter_map(|r| match r {
393 Ok(lookups) => Some(lookups),
394 Err(e) => {
395 error_count += 1;
396 debug!("Resolver lookup failed: {}", e);
397 None
398 }
399 })
400 .flatten()
401 .collect();
402
403 if error_count > 0 && lookups.is_empty() {
404 debug!("All {} resolver lookups failed with no results", error_count);
405 }
406
407 lookups.into()
408}
409
410#[doc(hidden)]
411impl From<resolv_conf::Config> for ResolverOpts {
412 fn from(config: resolv_conf::Config) -> Self {
413 ResolverOpts {
414 retries: config.attempts as usize,
415 ndots: config.ndots as usize,
416 timeout: Duration::from_secs(config.timeout as u64),
417 ..Default::default()
418 }
419 }
420}
421
422#[doc(hidden)]
423impl From<ResolverOpts> for hickory_resolver::config::ResolverOpts {
424 fn from(opts: ResolverOpts) -> Self {
425 let mut resolver_opts = hickory_resolver::config::ResolverOpts::default();
426 resolver_opts.attempts = opts.retries;
427 resolver_opts.ndots = opts.ndots;
428 resolver_opts.num_concurrent_reqs = opts.max_concurrent_requests;
429 resolver_opts.preserve_intermediates = opts.preserve_intermediates;
430 resolver_opts.timeout = opts.timeout;
431 resolver_opts
432 }
433}
434
435#[cfg(test)]
436impl Resolver {
437 pub fn new_for_test(opts: ResolverOpts, name_server: NameServerConfig) -> Self {
438 let config = ResolverConfig::new(name_server.clone());
439 let tr_opts: hickory_resolver::config::ResolverOpts = opts.clone().into();
440 let tr_config: hickory_resolver::config::ResolverConfig = config.into();
441 let tr_resolver = hickory_resolver::Resolver::builder_with_config(
442 tr_config,
443 hickory_resolver::name_server::TokioConnectionProvider::default(),
444 )
445 .with_options(tr_opts)
446 .build();
447
448 Resolver {
449 inner: Arc::new(tr_resolver),
450 opts: Arc::new(opts),
451 name_server: Arc::new(name_server),
452 }
453 }
454}
455
456#[doc(hidden)]
457impl From<ResolverConfig> for hickory_resolver::config::ResolverConfig {
458 fn from(rc: ResolverConfig) -> Self {
459 let mut config = Self::new();
460 config.add_name_server(rc.name_server_config.into());
461
462 config
463 }
464}