1use crate::config::Config;
2use crate::resolver::{DnsEntry, ResolvedIpAddrs, Resolver, Result};
3use std::fmt::{Display, Formatter};
4use std::net::IpAddr;
5use std::rc::Rc;
6
7#[derive(Debug, Copy, Clone, Eq, PartialEq)]
9pub enum ResolveMethod {
10 System,
12 Resolv,
14 Google,
16 Cloudflare,
18}
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq)]
22pub enum IpAddrFamily {
23 Ipv4Only,
25 Ipv6Only,
27 Ipv6thenIpv4,
29 Ipv4thenIpv6,
31 System,
34}
35
36impl Display for IpAddrFamily {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 match self {
39 Self::Ipv4Only => write!(f, "Ipv4Only"),
40 Self::Ipv6Only => write!(f, "Ipv6Only"),
41 Self::Ipv6thenIpv4 => write!(f, "Ipv6thenIpv4"),
42 Self::Ipv4thenIpv6 => write!(f, "Ipv4thenIpv6"),
43 Self::System => write!(f, "System"),
44 }
45 }
46}
47
48#[derive(Clone)]
50pub struct DnsResolver {
51 inner: Rc<inner::DnsResolver>,
52}
53
54impl DnsResolver {
55 pub fn start(config: Config) -> std::io::Result<Self> {
57 Ok(Self {
58 inner: Rc::new(inner::DnsResolver::start(config)?),
59 })
60 }
61
62 #[must_use]
64 pub fn config(&self) -> &Config {
65 self.inner.config()
66 }
67
68 pub fn flush(&self) {
70 self.inner.flush();
71 }
72}
73
74impl Resolver for DnsResolver {
75 fn lookup(&self, hostname: impl AsRef<str>) -> Result<ResolvedIpAddrs> {
76 self.inner.lookup(hostname.as_ref())
77 }
78 fn reverse_lookup(&self, addr: impl Into<IpAddr>) -> DnsEntry {
79 self.inner.reverse_lookup(addr.into(), false, false)
80 }
81 fn reverse_lookup_with_asinfo(&self, addr: impl Into<IpAddr>) -> DnsEntry {
82 self.inner.reverse_lookup(addr.into(), true, false)
83 }
84 fn lazy_reverse_lookup(&self, addr: impl Into<IpAddr>) -> DnsEntry {
85 self.inner.reverse_lookup(addr.into(), false, true)
86 }
87 fn lazy_reverse_lookup_with_asinfo(&self, addr: impl Into<IpAddr>) -> DnsEntry {
88 self.inner.reverse_lookup(addr.into(), true, true)
89 }
90}
91
92mod inner {
94 use super::{Config, IpAddrFamily, ResolveMethod};
95 use crate::resolver::{AsInfo, DnsEntry, Error, Resolved, ResolvedIpAddrs, Result, Unresolved};
96 use crossbeam::channel::{bounded, Receiver, Sender};
97 use hickory_resolver::config::{LookupIpStrategy, ResolverConfig, ResolverOpts};
98 use hickory_resolver::error::{ResolveError, ResolveErrorKind};
99 use hickory_resolver::proto::error::ProtoError;
100 use hickory_resolver::proto::rr::RecordType;
101 use hickory_resolver::system_conf::read_system_conf;
102 use hickory_resolver::{Name, Resolver};
103 use itertools::{Either, Itertools};
104 use parking_lot::RwLock;
105 use std::collections::HashMap;
106 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
107 use std::str::FromStr;
108 use std::sync::Arc;
109 use std::thread;
110 use std::time::{Duration, SystemTime};
111
112 const RESOLVER_MAX_QUEUE_SIZE: usize = 100;
114
115 const RESOLVER_QUEUE_TIMEOUT: Duration = Duration::from_millis(10);
118
119 type Cache = Arc<RwLock<HashMap<IpAddr, CacheEntry>>>;
121
122 #[derive(Debug, Clone)]
124 struct CacheEntry {
125 entry: DnsEntry,
127 timestamp: SystemTime,
129 }
130
131 impl CacheEntry {
132 const fn new(entry: DnsEntry, timestamp: SystemTime) -> Self {
133 Self { entry, timestamp }
134 }
135
136 fn set_timestamp(&mut self, timestamp: SystemTime) {
137 self.timestamp = timestamp;
138 }
139 }
140
141 #[derive(Clone)]
142 enum DnsProvider {
143 TrustDns(Arc<Resolver>),
144 DnsLookup,
145 }
146
147 #[derive(Debug, Clone)]
148 struct DnsResolveRequest {
149 addr: IpAddr,
150 with_asinfo: bool,
151 }
152
153 pub(super) struct DnsResolver {
155 config: Config,
156 provider: DnsProvider,
157 tx: Sender<DnsResolveRequest>,
158 addr_cache: Cache,
159 }
160
161 impl DnsResolver {
162 pub(super) fn start(config: Config) -> std::io::Result<Self> {
163 let (tx, rx) = bounded(RESOLVER_MAX_QUEUE_SIZE);
164 let addr_cache = Arc::new(RwLock::new(HashMap::new()));
165
166 let provider = if matches!(config.resolve_method, ResolveMethod::System) {
167 DnsProvider::DnsLookup
168 } else {
169 let mut options = ResolverOpts::default();
170 #[allow(clippy::match_same_arms)]
171 let ip_strategy = match config.addr_family {
172 IpAddrFamily::Ipv4Only => LookupIpStrategy::Ipv4Only,
173 IpAddrFamily::Ipv6Only => LookupIpStrategy::Ipv6Only,
174 IpAddrFamily::Ipv6thenIpv4 => LookupIpStrategy::Ipv6thenIpv4,
175 IpAddrFamily::Ipv4thenIpv6 => LookupIpStrategy::Ipv4thenIpv6,
176 IpAddrFamily::System => LookupIpStrategy::Ipv6thenIpv4,
178 };
179 options.timeout = config.timeout;
180 options.ip_strategy = ip_strategy;
181 let res = match config.resolve_method {
182 ResolveMethod::Resolv => {
183 let (resolver_cfg, mut options) = read_system_conf()?;
184 options.timeout = config.timeout;
185 options.ip_strategy = ip_strategy;
186 Resolver::new(resolver_cfg, options)
187 }
188 ResolveMethod::Google => Resolver::new(ResolverConfig::google(), options),
189 ResolveMethod::Cloudflare => {
190 Resolver::new(ResolverConfig::cloudflare(), options)
191 }
192 ResolveMethod::System => unreachable!(),
193 }?;
194 let resolver = Arc::new(res);
195 DnsProvider::TrustDns(resolver)
196 };
197
198 {
200 let cache = addr_cache.clone();
201 let provider = provider.clone();
202 thread::spawn(move || resolver_queue_processor(rx, &provider, &cache));
203 }
204 Ok(Self {
205 config,
206 provider,
207 tx,
208 addr_cache,
209 })
210 }
211
212 pub(super) const fn config(&self) -> &Config {
213 &self.config
214 }
215
216 pub(super) fn lookup(&self, hostname: &str) -> Result<ResolvedIpAddrs> {
217 fn partition(all: Vec<IpAddr>) -> (Vec<IpAddr>, Vec<IpAddr>) {
218 all.into_iter().partition_map(|ip| match ip {
219 IpAddr::V4(_) => Either::Left(ip),
220 IpAddr::V6(_) => Either::Right(ip),
221 })
222 }
223 match &self.provider {
224 DnsProvider::TrustDns(resolver) => Ok(resolver
225 .lookup_ip(hostname)
226 .map_err(|err| Error::LookupFailed(Box::new(err)))?
227 .iter()
228 .collect::<Vec<_>>()),
229 DnsProvider::DnsLookup => {
230 let all = dns_lookup::lookup_host(hostname)
231 .map_err(|err| Error::LookupFailed(Box::new(err)))?;
232 Ok(match self.config.addr_family {
233 IpAddrFamily::Ipv4Only => {
234 let (ipv4, _) = partition(all);
235 if ipv4.is_empty() {
236 vec![]
237 } else {
238 ipv4
239 }
240 }
241 IpAddrFamily::Ipv6Only => {
242 let (_, ipv6) = partition(all);
243 if ipv6.is_empty() {
244 vec![]
245 } else {
246 ipv6
247 }
248 }
249 IpAddrFamily::Ipv6thenIpv4 => {
250 let (ipv4, ipv6) = partition(all);
251 if ipv6.is_empty() {
252 ipv4
253 } else {
254 ipv6
255 }
256 }
257 IpAddrFamily::Ipv4thenIpv6 => {
258 let (ipv4, ipv6) = partition(all);
259 if ipv4.is_empty() {
260 ipv6
261 } else {
262 ipv4
263 }
264 }
265 IpAddrFamily::System => all,
266 })
267 }
268 }
269 .map(ResolvedIpAddrs)
270 }
271
272 pub(super) fn reverse_lookup(
273 &self,
274 addr: IpAddr,
275 with_asinfo: bool,
276 lazy: bool,
277 ) -> DnsEntry {
278 if lazy {
279 self.lazy_reverse_lookup(addr, with_asinfo).entry
280 } else {
281 reverse_lookup(&self.provider, addr, with_asinfo).entry
282 }
283 }
284
285 fn lazy_reverse_lookup(&self, addr: IpAddr, with_asinfo: bool) -> CacheEntry {
286 let mut enqueue = false;
287 let now = SystemTime::now();
288
289 let mut dns_entry = self
292 .addr_cache
293 .write()
294 .entry(addr)
295 .or_insert_with(|| {
296 enqueue = true;
297 CacheEntry::new(DnsEntry::Pending(addr), now)
298 })
299 .clone();
300
301 match &dns_entry.entry {
305 DnsEntry::Resolved(_) | DnsEntry::NotFound(_) | DnsEntry::Failed(_) => {
306 if now.duration_since(dns_entry.timestamp).unwrap_or_default() > self.config.ttl
307 {
308 self.addr_cache
309 .write()
310 .get_mut(&addr)
311 .expect("addr must be in cache")
312 .set_timestamp(now);
313 enqueue = true;
314 }
315 }
316 _ => {}
317 }
318
319 if let DnsEntry::Timeout(addr) = dns_entry.entry {
322 *self
323 .addr_cache
324 .write()
325 .get_mut(&addr)
326 .expect("addr must be in cache") =
327 CacheEntry::new(DnsEntry::Pending(addr), now);
328 dns_entry = CacheEntry::new(DnsEntry::Pending(addr), now);
329 enqueue = true;
330 }
331
332 if enqueue {
336 if self
337 .tx
338 .send_timeout(
339 DnsResolveRequest { addr, with_asinfo },
340 RESOLVER_QUEUE_TIMEOUT,
341 )
342 .is_ok()
343 {
344 dns_entry
345 } else {
346 *self
347 .addr_cache
348 .write()
349 .get_mut(&addr)
350 .expect("addr must be in cache") =
351 CacheEntry::new(DnsEntry::Timeout(addr), now);
352 CacheEntry::new(DnsEntry::Timeout(addr), now)
353 }
354 } else {
355 dns_entry
356 }
357 }
358
359 pub fn flush(&self) {
360 self.addr_cache.write().clear();
361 }
362 }
363
364 fn resolver_queue_processor(
369 rx: Receiver<DnsResolveRequest>,
370 provider: &DnsProvider,
371 cache: &Cache,
372 ) {
373 for DnsResolveRequest { addr, with_asinfo } in rx {
374 let dns_entry = reverse_lookup(provider, addr, with_asinfo);
375 cache.write().insert(addr, dns_entry);
376 }
377 }
378
379 fn reverse_lookup(provider: &DnsProvider, addr: IpAddr, with_asinfo: bool) -> CacheEntry {
380 let now = SystemTime::now();
381 match &provider {
382 DnsProvider::DnsLookup => {
383 match dns_lookup::lookup_addr(&addr) {
386 Ok(dns) => {
387 CacheEntry::new(DnsEntry::Resolved(Resolved::Normal(addr, vec![dns])), now)
388 }
389 Err(_) => CacheEntry::new(DnsEntry::NotFound(Unresolved::Normal(addr)), now),
390 }
391 }
392 DnsProvider::TrustDns(resolver) => match resolver.reverse_lookup(addr) {
393 Ok(name) => {
394 let hostnames = name
395 .into_iter()
396 .map(|mut s| {
397 s.0.set_fqdn(false);
398 s
399 })
400 .map(|s| s.to_string())
401 .collect();
402 if with_asinfo {
403 let as_info = lookup_asinfo(resolver, addr).unwrap_or_default();
404 CacheEntry::new(
405 DnsEntry::Resolved(Resolved::WithAsInfo(addr, hostnames, as_info)),
406 now,
407 )
408 } else {
409 CacheEntry::new(DnsEntry::Resolved(Resolved::Normal(addr, hostnames)), now)
410 }
411 }
412 Err(err) => match err.kind() {
413 ResolveErrorKind::NoRecordsFound { .. } => {
414 if with_asinfo {
415 let as_info = lookup_asinfo(resolver, addr).unwrap_or_default();
416 CacheEntry::new(
417 DnsEntry::NotFound(Unresolved::WithAsInfo(addr, as_info)),
418 now,
419 )
420 } else {
421 CacheEntry::new(DnsEntry::NotFound(Unresolved::Normal(addr)), now)
422 }
423 }
424 ResolveErrorKind::Timeout => CacheEntry::new(DnsEntry::Timeout(addr), now),
425 _ => CacheEntry::new(DnsEntry::Failed(addr), now),
426 },
427 },
428 }
429 }
430
431 fn lookup_asinfo(resolver: &Arc<Resolver>, addr: IpAddr) -> Result<AsInfo> {
433 let origin_query_txt = match addr {
434 IpAddr::V4(addr) => query_asn_ipv4(resolver, addr)?,
435 IpAddr::V6(addr) => query_asn_ipv6(resolver, addr)?,
436 };
437 let asinfo = parse_origin_query_txt(&origin_query_txt)?;
438 let asn_query_txt = query_asn_name(resolver, &asinfo.asn)?;
439 let as_name = parse_asn_query_txt(&asn_query_txt)?;
440 Ok(AsInfo {
441 asn: asinfo.asn,
442 prefix: asinfo.prefix,
443 cc: asinfo.cc,
444 registry: asinfo.registry,
445 allocated: asinfo.allocated,
446 name: as_name,
447 })
448 }
449
450 fn query_asn_ipv4(resolver: &Arc<Resolver>, addr: Ipv4Addr) -> Result<String> {
452 let query = format!(
453 "{}.origin.asn.cymru.com.",
454 addr.octets().iter().rev().join(".")
455 );
456 let name = Name::from_str(query.as_str()).map_err(proto_error)?;
457 let response = resolver
458 .lookup(name, RecordType::TXT)
459 .map_err(resolve_error)?;
460 let data = response
461 .iter()
462 .next()
463 .ok_or_else(|| Error::QueryAsnOriginFailed)?;
464 let bytes = data.as_txt().ok_or_else(|| Error::QueryAsnOriginFailed)?;
465 Ok(bytes.to_string())
466 }
467
468 fn query_asn_ipv6(resolver: &Arc<Resolver>, addr: Ipv6Addr) -> Result<String> {
470 let query = format!(
471 "{:x}.origin6.asn.cymru.com.",
472 addr.octets()
473 .iter()
474 .rev()
475 .flat_map(|o| [o & 0x0F, (o & 0xF0) >> 4])
476 .format(".")
477 );
478 let name = Name::from_str(query.as_str()).map_err(proto_error)?;
479 let response = resolver
480 .lookup(name, RecordType::TXT)
481 .map_err(resolve_error)?;
482 let data = response
483 .iter()
484 .next()
485 .ok_or_else(|| Error::QueryAsnOriginFailed)?;
486 let bytes = data.as_txt().ok_or_else(|| Error::QueryAsnOriginFailed)?;
487 Ok(bytes.to_string())
488 }
489
490 fn query_asn_name(resolver: &Arc<Resolver>, asn: &str) -> Result<String> {
492 let query = format!("AS{asn}.asn.cymru.com.");
493 let name = Name::from_str(query.as_str()).map_err(proto_error)?;
494 let response = resolver
495 .lookup(name, RecordType::TXT)
496 .map_err(resolve_error)?;
497 let data = response
498 .iter()
499 .next()
500 .ok_or_else(|| Error::QueryAsnFailed)?;
501 let bytes = data.as_txt().ok_or_else(|| Error::QueryAsnFailed)?;
502 Ok(bytes.to_string())
503 }
504
505 fn parse_origin_query_txt(origin_query_txt: &str) -> Result<AsInfo> {
513 if origin_query_txt.chars().filter(|c| *c == '|').count() != 4 {
514 return Err(Error::ParseOriginQueryFailed(String::from(
515 origin_query_txt,
516 )));
517 }
518 let mut split = origin_query_txt.split('|');
519 let asn = split.next().unwrap_or_default().trim().to_string();
520 let prefix = split.next().unwrap_or_default().trim().to_string();
521 let cc = split.next().unwrap_or_default().trim().to_string();
522 let registry = split.next().unwrap_or_default().trim().to_string();
523 let allocated = split.next().unwrap_or_default().trim().to_string();
524 Ok(AsInfo {
525 asn,
526 prefix,
527 cc,
528 registry,
529 allocated,
530 name: String::default(),
531 })
532 }
533
534 fn parse_asn_query_txt(asn_query_txt: &str) -> Result<String> {
542 if asn_query_txt.chars().filter(|c| *c == '|').count() != 4 {
543 return Err(Error::ParseAsnQueryFailed(String::from(asn_query_txt)));
544 }
545 let mut split = asn_query_txt.split('|');
546 Ok(split.nth(4).unwrap_or_default().trim().to_string())
547 }
548
549 fn resolve_error(err: ResolveError) -> Error {
551 Error::LookupFailed(Box::new(err))
552 }
553
554 fn proto_error(err: ProtoError) -> Error {
556 Error::LookupFailed(Box::new(err))
557 }
558}