2018-03-07 08:27:19 +00:00
|
|
|
use dnsbox_base::common_types::{Type, DnsName};
|
2019-07-01 15:43:34 +00:00
|
|
|
use failure::Error;
|
2018-03-07 08:27:19 +00:00
|
|
|
use dnsbox_base::ser::RRData;
|
|
|
|
use futures::{Future, Poll, Async};
|
|
|
|
use futures::unsync::oneshot;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::mem::replace;
|
|
|
|
use std::rc::Rc;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
mod root_hints;
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
|
|
|
pub enum Source {
|
|
|
|
Hint,
|
|
|
|
Glue,
|
|
|
|
Authority,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct Entry {
|
2019-09-08 13:34:28 +00:00
|
|
|
rrset: Vec<Box<dyn RRData>>,
|
2018-03-07 08:27:19 +00:00
|
|
|
source: Source,
|
|
|
|
ttl: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Entry {
|
2019-09-08 13:34:28 +00:00
|
|
|
pub fn new(rrset: Vec<Box<dyn RRData>>, source: Source) -> Self {
|
2018-03-07 08:27:19 +00:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// whether TTL not reached yet. might also check DNSSEC stamps
|
|
|
|
pub fn alive(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn needs_refresh(&self) -> bool {
|
|
|
|
// TTL > 60 and only 25% ttl left before getting invalid
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum InnerEntry {
|
|
|
|
Missing,
|
|
|
|
Hit(Rc<Entry>),
|
|
|
|
Refreshing(Rc<Entry>),
|
|
|
|
Pending(Vec<oneshot::Sender<Rc<Entry>>>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InnerEntry {
|
|
|
|
fn restart(&mut self) -> (bool, CacheResult) {
|
|
|
|
// start new lookup
|
|
|
|
let (tx, rx) = oneshot::channel();
|
|
|
|
replace(self, InnerEntry::Pending(vec![tx]));
|
|
|
|
(true, CacheResult(InnerResult::Waiting(rx)))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get(&mut self) -> (bool, CacheResult) {
|
|
|
|
match replace(self, InnerEntry::Missing) {
|
|
|
|
InnerEntry::Missing => self.restart(),
|
|
|
|
InnerEntry::Hit(e) => {
|
|
|
|
if !e.alive() {
|
|
|
|
self.restart();
|
|
|
|
}
|
|
|
|
let res = CacheResult(InnerResult::Ready(e.clone()));
|
|
|
|
let start_lookup = if e.needs_refresh() {
|
|
|
|
replace(self, InnerEntry::Refreshing(e));
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
replace(self, InnerEntry::Hit(e));
|
|
|
|
false
|
|
|
|
};
|
|
|
|
(start_lookup, res)
|
|
|
|
},
|
|
|
|
InnerEntry::Refreshing(e) => {
|
|
|
|
if !e.alive() {
|
|
|
|
// new request already pending; but need new wait queue
|
|
|
|
(false, self.restart().1)
|
|
|
|
} else {
|
|
|
|
let res = CacheResult(InnerResult::Ready(e.clone()));
|
|
|
|
replace(self, InnerEntry::Refreshing(e));
|
|
|
|
(false, res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
InnerEntry::Pending(mut queue) => {
|
|
|
|
let (tx, rx) = oneshot::channel();
|
|
|
|
queue.push(tx);
|
|
|
|
replace(self, InnerEntry::Pending(queue));
|
|
|
|
(false, CacheResult(InnerResult::Waiting(rx)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type NameEntry = HashMap<Type, InnerEntry>;
|
|
|
|
|
2019-09-08 13:34:28 +00:00
|
|
|
pub type LookupFunction = Box<dyn FnMut(DnsName) -> Box<dyn Future<Item = Entry, Error = Error>>>;
|
2018-03-07 08:27:19 +00:00
|
|
|
|
|
|
|
pub struct Cache {
|
|
|
|
names: Rc<RefCell<HashMap<String, NameEntry>>>,
|
|
|
|
timeout: Duration,
|
|
|
|
lookup: RefCell<LookupFunction>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Cache {
|
|
|
|
pub fn new(timeout: Duration, lookup: LookupFunction) -> Self {
|
|
|
|
Cache {
|
|
|
|
names: Rc::new(RefCell::new(HashMap::new())),
|
|
|
|
timeout: timeout,
|
|
|
|
lookup: RefCell::new(lookup),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start_lookup(&self, name: DnsName, rrtype: Type) {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lookup(&self, name: DnsName, rrtype: Type) -> CacheResult {
|
|
|
|
let mut name_str = format!("{}", name);
|
|
|
|
name_str.make_ascii_lowercase();
|
|
|
|
|
|
|
|
let (start_lookup, res) = {
|
|
|
|
let mut names = self.names.borrow_mut();
|
|
|
|
let types = names.entry(name_str).or_insert_with(|| HashMap::new());
|
|
|
|
let entry = types.entry(rrtype).or_insert(InnerEntry::Missing);
|
|
|
|
entry.get()
|
|
|
|
};
|
|
|
|
|
|
|
|
if start_lookup {
|
|
|
|
self.start_lookup(name, rrtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert hints
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics when the `rrset` entries don't match `rrtype`.
|
2019-09-08 13:34:28 +00:00
|
|
|
pub fn insert_hint(&self, name: DnsName, rrtype: Type, rrset: Vec<Box<dyn RRData>>) {
|
2018-03-07 08:27:19 +00:00
|
|
|
for e in &rrset {
|
|
|
|
assert_eq!(rrtype, e.rr_type());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct CacheResult(InnerResult);
|
|
|
|
|
|
|
|
enum InnerResult {
|
|
|
|
Ready(Rc<Entry>),
|
|
|
|
Waiting(oneshot::Receiver<Rc<Entry>>),
|
|
|
|
Finished,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Future for CacheResult {
|
|
|
|
type Item = Rc<Entry>;
|
|
|
|
type Error = Error;
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
|
|
|
match replace(&mut self.0, InnerResult::Finished) {
|
|
|
|
InnerResult::Ready(res) => Ok(Async::Ready(res)),
|
|
|
|
InnerResult::Waiting(mut rc) => {
|
|
|
|
match rc.poll()? {
|
|
|
|
Async::Ready(res) => Ok(Async::Ready(res)),
|
|
|
|
Async::NotReady => {
|
|
|
|
// keep waiting
|
|
|
|
replace(&mut self.0, InnerResult::Waiting(rc));
|
|
|
|
Ok(Async::NotReady)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
InnerResult::Finished => Ok(Async::NotReady),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|