use super::{Mx, Negative, RecordSet, TxtRecord};
use crate::Instant;
use std::{
borrow::Borrow,
hash::Hash,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
pub trait ResolverCache<K, V>: Sized {
fn get<Q>(&self, name: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized;
fn remove<Q>(&self, name: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized;
fn insert(&self, key: K, value: V, valid_until: Instant);
}
pub trait DnsCache {
type Txt: ResolverCache<Box<str>, TxtRecord>;
type Mx: ResolverCache<Box<str>, Result<RecordSet<Mx>, Negative>>;
type Ipv4: ResolverCache<Box<str>, Result<RecordSet<Ipv4Addr>, Negative>>;
type Ipv6: ResolverCache<Box<str>, Result<RecordSet<Ipv6Addr>, Negative>>;
type Ptr: ResolverCache<IpAddr, Result<RecordSet<Box<str>>, Negative>>;
fn txt(&self) -> Option<&Self::Txt>;
fn mx(&self) -> Option<&Self::Mx>;
fn ipv4(&self) -> Option<&Self::Ipv4>;
fn ipv6(&self) -> Option<&Self::Ipv6>;
fn ptr(&self) -> Option<&Self::Ptr>;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct NoCache;
impl<K, V> ResolverCache<K, V> for NoCache {
fn get<Q>(&self, _: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
None
}
fn remove<Q>(&self, _: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
None
}
fn insert(&self, _: K, _: V, _: Instant) {}
}
impl DnsCache for NoCache {
type Txt = NoCache;
type Mx = NoCache;
type Ipv4 = NoCache;
type Ipv6 = NoCache;
type Ptr = NoCache;
fn txt(&self) -> Option<&Self::Txt> {
None
}
fn mx(&self) -> Option<&Self::Mx> {
None
}
fn ipv4(&self) -> Option<&Self::Ipv4> {
None
}
fn ipv6(&self) -> Option<&Self::Ipv6> {
None
}
fn ptr(&self) -> Option<&Self::Ptr> {
None
}
}
pub struct Parameters<'x, P, C: DnsCache = NoCache> {
pub input: P,
pub cache: Option<&'x C>,
}
impl<P> Parameters<'_, P, NoCache> {
pub fn new(input: P) -> Self {
Parameters { input, cache: None }
}
}
impl<'x, P, C: DnsCache> Parameters<'x, P, C> {
pub fn with_cache<NewC: DnsCache>(self, cache: &'x NewC) -> Parameters<'x, P, NewC> {
Parameters {
input: self.input,
cache: Some(cache),
}
}
pub fn clone_with<NewP>(&self, input: NewP) -> Parameters<'x, NewP, C> {
Parameters {
input,
cache: self.cache,
}
}
#[inline(always)]
pub(crate) fn txt_cache(&self) -> Option<&'x C::Txt> {
self.cache.and_then(DnsCache::txt)
}
#[inline(always)]
pub(crate) fn mx_cache(&self) -> Option<&'x C::Mx> {
self.cache.and_then(DnsCache::mx)
}
#[inline(always)]
pub(crate) fn ipv4_cache(&self) -> Option<&'x C::Ipv4> {
self.cache.and_then(DnsCache::ipv4)
}
#[inline(always)]
pub(crate) fn ipv6_cache(&self) -> Option<&'x C::Ipv6> {
self.cache.and_then(DnsCache::ipv6)
}
#[inline(always)]
pub(crate) fn ptr_cache(&self) -> Option<&'x C::Ptr> {
self.cache.and_then(DnsCache::ptr)
}
}
#[cfg(test)]
pub(crate) mod test {
use crate::dns::{
DnsCache, DnssecStatus, Mx, Negative, Parameters, RecordSet, ResolverCache, ToFqdn,
TxtRecord,
};
use std::{
borrow::Borrow,
hash::Hash,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
sync::Arc,
};
pub(crate) struct DummyCache<K, V>(std::sync::Mutex<std::collections::HashMap<K, V>>);
impl<K: Hash + Eq, V: Clone> DummyCache<K, V> {
pub fn new() -> Self {
DummyCache(std::sync::Mutex::new(std::collections::HashMap::new()))
}
}
impl<K: Hash + Eq, V: Clone> ResolverCache<K, V> for DummyCache<K, V> {
fn get<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.0.lock().unwrap().get(key).cloned()
}
fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.0.lock().unwrap().remove(key)
}
fn insert(&self, key: K, value: V, _: std::time::Instant) {
self.0.lock().unwrap().insert(key, value);
}
}
type Answer<T> = Result<RecordSet<T>, Negative>;
pub(crate) struct DummyCaches {
pub txt: DummyCache<Box<str>, TxtRecord>,
pub mx: DummyCache<Box<str>, Answer<Mx>>,
pub ptr: DummyCache<IpAddr, Answer<Box<str>>>,
pub ipv4: DummyCache<Box<str>, Answer<Ipv4Addr>>,
pub ipv6: DummyCache<Box<str>, Answer<Ipv6Addr>>,
}
impl DummyCaches {
pub fn new() -> Self {
Self {
txt: DummyCache::new(),
mx: DummyCache::new(),
ptr: DummyCache::new(),
ipv4: DummyCache::new(),
ipv6: DummyCache::new(),
}
}
pub fn with_txt(
self,
name: impl ToFqdn,
value: impl Into<TxtRecord>,
valid_until: std::time::Instant,
) -> Self {
self.txt.insert(
name.to_fqdn().into_owned().into_boxed_str(),
value.into(),
valid_until,
);
self
}
pub fn txt_add(
&self,
name: impl ToFqdn,
value: impl Into<TxtRecord>,
valid_until: std::time::Instant,
) {
self.txt.insert(
name.to_fqdn().into_owned().into_boxed_str(),
value.into(),
valid_until,
);
}
pub fn ipv4_add(
&self,
name: impl ToFqdn,
value: Vec<Ipv4Addr>,
valid_until: std::time::Instant,
) {
self.ipv4.insert(
name.to_fqdn().into_owned().into_boxed_str(),
Ok(RecordSet {
records: Arc::from(value.into_boxed_slice()),
dnssec_status: DnssecStatus::Indeterminate,
}),
valid_until,
);
}
pub fn ipv6_add(
&self,
name: impl ToFqdn,
value: Vec<Ipv6Addr>,
valid_until: std::time::Instant,
) {
self.ipv6.insert(
name.to_fqdn().into_owned().into_boxed_str(),
Ok(RecordSet {
records: Arc::from(value.into_boxed_slice()),
dnssec_status: DnssecStatus::Indeterminate,
}),
valid_until,
);
}
pub fn ptr_add(&self, name: IpAddr, value: Vec<Box<str>>, valid_until: std::time::Instant) {
self.ptr.insert(
name,
Ok(RecordSet {
records: Arc::from(value.into_boxed_slice()),
dnssec_status: DnssecStatus::Indeterminate,
}),
valid_until,
);
}
pub fn mx_add(&self, name: impl ToFqdn, value: Vec<Mx>, valid_until: std::time::Instant) {
self.mx.insert(
name.to_fqdn().into_owned().into_boxed_str(),
Ok(RecordSet {
records: Arc::from(value.into_boxed_slice()),
dnssec_status: DnssecStatus::Indeterminate,
}),
valid_until,
);
}
pub fn parameters<T>(&self, param: T) -> Parameters<'_, T, DummyCaches> {
Parameters::new(param).with_cache(self)
}
}
impl DnsCache for DummyCaches {
type Txt = DummyCache<Box<str>, TxtRecord>;
type Mx = DummyCache<Box<str>, Answer<Mx>>;
type Ipv4 = DummyCache<Box<str>, Answer<Ipv4Addr>>;
type Ipv6 = DummyCache<Box<str>, Answer<Ipv6Addr>>;
type Ptr = DummyCache<IpAddr, Answer<Box<str>>>;
fn txt(&self) -> Option<&Self::Txt> {
Some(&self.txt)
}
fn mx(&self) -> Option<&Self::Mx> {
Some(&self.mx)
}
fn ipv4(&self) -> Option<&Self::Ipv4> {
Some(&self.ipv4)
}
fn ipv6(&self) -> Option<&Self::Ipv6> {
Some(&self.ipv6)
}
fn ptr(&self) -> Option<&Self::Ptr> {
Some(&self.ptr)
}
}
}