Futurize the RequestHandler and use AsyncResolver

This commit is contained in:
Benjamin Fry 2019-02-19 22:39:57 -08:00
parent c06ea4df2c
commit 58b13f9e53
48 changed files with 1695 additions and 694 deletions

View File

@ -7,17 +7,25 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- UDP Sockets not being properly closed in timeout scenarios #635
- (proto) UDP Sockets not being properly closed in timeout scenarios #635
### Added
- support for the OPENPGPKEY and SSHFP record types #646 #647
- (proto) support for the OPENPGPKEY and SSHFP record types #646 #647
- (server) forwarding support in server with trust-dns-resolver (default feature) #674
- (server) Authority trait for generic Authorities (File, Sqlite, Forwarder) #674
### Changed
- *breaking* UdpClientStream and UdpClientConnection refactored to associate UDP sockets to single requests #635
- *breaking* configuration for sqlite dynamic update different, see dnssec_with_update.toml for example #622
- *breaking* util/dnskey_to_pem has been renamed to bind_dnskey_to_pem for clarity #622
- *breaking* (proto) UdpClientStream and UdpClientConnection refactored to associate UDP sockets to single requests #635
- *breaking* (server) configuration for sqlite dynamic update different, see dnssec_with_update.toml for example #622
- *breaking* (util)/dnskey_to_pem has been renamed to bind_dnskey_to_pem for clarity #622
- *breaking* (proto) Record::from_rdata no longer requires RecordType parameter #674
- *breaking* (server) AuthLookup inner types simplified #674
- *breaking* (server) RequestHandler now requires associated type for Future results of lookups #674
- *breaking* (server) ResponseHandler now requires Clone and 'static #674
- *breaking* (server) Catalog::lookup takes ownership of MessageRequest and returns a LookupFuture #674
- *breaking* (server) MessageRequest and Queries no longer carrying lifetime parameters #674
## 0.15.0

View File

@ -1,4 +1,4 @@
// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
@ -252,6 +252,12 @@ impl From<LowerName> for Name {
}
}
impl<'a> From<&'a LowerName> for Name {
fn from(name: &'a LowerName) -> Self {
name.0.clone()
}
}
impl Borrow<Name> for LowerName {
fn borrow(&self) -> &Name {
&self.0

View File

@ -1144,19 +1144,6 @@ pub trait IntoName: Sized {
fn into_name(self) -> ProtoResult<Name>;
}
impl<'a> IntoName for &'a Name {
/// Clones this into a new `Name`
fn into_name(self) -> ProtoResult<Name> {
Ok(self.clone())
}
}
impl IntoName for Name {
fn into_name(self) -> ProtoResult<Name> {
Ok(self.clone())
}
}
impl<'a> IntoName for &'a str {
/// Performs a utf8, IDNA or punycode, translation of the `str` into `Name`
fn into_name(self) -> ProtoResult<Name> {
@ -1171,6 +1158,15 @@ impl IntoName for String {
}
}
impl<T> IntoName for T
where
T: Into<Name>,
{
fn into_name(self) -> ProtoResult<Name> {
Ok(self.into())
}
}
#[cfg(feature = "serde-config")]
impl Serialize for Name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -1537,10 +1533,7 @@ mod tests {
#[test]
fn test_into_name() {
let name = Name::from_utf8("www.example.com").unwrap();
assert_eq!(
Name::from_utf8("www.example.com").unwrap(),
(&name).into_name().unwrap()
);
assert_eq!(Name::from_utf8("www.example.com").unwrap(), name);
assert_eq!(
Name::from_utf8("www.example.com").unwrap(),
Name::from_utf8("www.example.com")

View File

@ -115,7 +115,6 @@ impl Record {
/// # Arguments
///
/// * `name` - name of the resource records
/// * `rr_type` - the record type
/// * `ttl` - time-to-live is the amount of time this record should be cached before refreshing
/// * `rdata` - record data to associate with the Record
pub fn from_rdata(name: Name, ttl: u32, rdata: RData) -> Record {

View File

@ -12,10 +12,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added
- New option to execute queries concurrently, default is 2 #615
- Lookup::record_iter for listing all records returned in request #674
### Changed
- Added option to distrust Nameservers on SERVFAIL responses, continue resolution #613
- *breaking* Record::from_rdata no longer requires RecordType parameter #674
- LRU cache is now based on Query rather than just name #674
## 0.10.2

View File

@ -36,6 +36,8 @@ pub(super) fn task(
request_rx: mpsc::UnboundedReceiver<Request>,
) -> impl Future<Item = (), Error = ()> {
future::lazy(move || {
debug!("trust-dns resolver running");
let pool =
NameServerPool::<ConnectionHandle, StandardConnection>::from_config(&config, &options);
let either;

View File

@ -135,7 +135,8 @@ impl ServiceInfo {
} else {
None
}
}).collect()
})
.collect()
}
}
@ -168,14 +169,14 @@ mod tests {
for name in response.iter() {
println!("service: {}", name);
let srvs = io_loop
.block_on(resolver.lookup_srv(name))
.block_on(resolver.lookup_srv(name.clone()))
.expect("failed to lookup name");
for srv in srvs.iter() {
println!("service: {:#?}", srv);
let info = io_loop
.block_on(resolver.service_info(name))
.block_on(resolver.service_info(name.clone()))
.expect("info failed");
let info = info.to_map();
println!("info: {:#?}", info);

View File

@ -103,7 +103,7 @@ trust-dns = { version = "0.16.0-alpha", path = "../client" }
trust-dns-https = { version = "0.3.0", path = "../https", optional = true }
trust-dns-proto = { version = "0.7.0", path = "../proto" }
trust-dns-openssl = { version = "0.6.0", path = "../openssl", optional = true }
trust-dns-resolver = { version = "0.11.0-alpha", path = "../resolver", optional = true }
trust-dns-resolver = { version = "0.11.0-alpha", path = "../resolver", features = ["serde-config"], optional = true }
trust-dns-rustls = { version = "0.6.0", path = "../rustls", optional = true }
[dev-dependencies]

View File

@ -56,30 +56,6 @@ impl AuthLookup {
self.iter().count() == 0
}
/// This is a non-existant domain name
pub fn is_nx_domain(&self) -> bool {
match *self {
AuthLookup::NxDomain => true,
_ => false,
}
}
/// This is a NameExists
pub fn is_name_exists(&self) -> bool {
match *self {
AuthLookup::NameExists => true,
_ => false,
}
}
/// This is a non-existant domain name
pub fn is_refused(&self) -> bool {
match *self {
AuthLookup::Refused => true,
_ => false,
}
}
/// Conversion to an iterator
pub fn iter(&self) -> AuthLookupIter {
self.into_iter()
@ -281,6 +257,7 @@ impl<'r> Iterator for AnyRecordsIter<'r> {
/// The result of a lookup
#[derive(Debug)]
pub enum LookupRecords {
/// The empty set of records
Empty,
/// The associate records
Records {
@ -316,7 +293,7 @@ impl LookupRecords {
pub fn many(
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
records: Vec<Arc<RecordSet>>
records: Vec<Arc<RecordSet>>,
) -> Self {
LookupRecords::ManyRecords(is_secure, supported_algorithms, records)
}
@ -353,7 +330,12 @@ impl<'a> IntoIterator for &'a LookupRecords {
records,
} => LookupRecordsIter::RecordsIter(records.records(*is_secure, *supported_algorithms)),
LookupRecords::ManyRecords(is_secure, supported_algorithms, r) => {
LookupRecordsIter::ManyRecordsIter(r.iter().map(|r| r.records(*is_secure, *supported_algorithms)).collect(), None)
LookupRecordsIter::ManyRecordsIter(
r.iter()
.map(|r| r.records(*is_secure, *supported_algorithms))
.collect(),
None,
)
}
LookupRecords::AnyRecords(r) => LookupRecordsIter::AnyRecordsIter(r.iter()),
}
@ -382,23 +364,21 @@ impl<'r> Iterator for LookupRecordsIter<'r> {
type Item = &'r Record;
fn next(&mut self) -> Option<Self::Item> {
match self {
LookupRecordsIter::Empty => None,
LookupRecordsIter::AnyRecordsIter(current) => current.next(),
LookupRecordsIter::RecordsIter(current) => current.next(),
LookupRecordsIter::ManyRecordsIter(set, ref mut current) => {
loop {
if let Some(o) = current.as_mut().and_then(|o| o.next()) {
return Some(o)
}
*current = set.pop();
if current.is_none() {
return None
}
}
match self {
LookupRecordsIter::Empty => None,
LookupRecordsIter::AnyRecordsIter(current) => current.next(),
LookupRecordsIter::RecordsIter(current) => current.next(),
LookupRecordsIter::ManyRecordsIter(set, ref mut current) => loop {
if let Some(o) = current.as_mut().and_then(|o| o.next()) {
return Some(o);
}
}
*current = set.pop();
if current.is_none() {
return None;
}
},
}
}
}

View File

@ -7,17 +7,21 @@
//! All authority related types
use futures::Future;
use trust_dns::op::LowerQuery;
use trust_dns::proto::rr::dnssec::rdata::key::KEY;
use trust_dns::rr::dnssec::{DnsSecError, DnsSecResult, Signer, SupportedAlgorithms};
use trust_dns::rr::{LowerName, Name, RecordType};
use authority::{LookupResult, MessageRequest, UpdateResult, ZoneType};
use authority::{LookupError, MessageRequest, UpdateResult, ZoneType};
/// Authority implementations can be used with a `Catalog`
pub trait Authority: Send {
/// Result of a lookup
type Lookup;
type Lookup: Send + Sized + 'static;
/// The future type that will resolve to a Lookup
type LookupFuture: Future<Item = Self::Lookup, Error = LookupError> + Send;
/// What type is this zone
fn zone_type(&self) -> ZoneType;
@ -51,7 +55,7 @@ pub trait Authority: Send {
rtype: RecordType,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup>;
) -> Self::LookupFuture;
/// Using the specified query, perform a lookup against this zone.
///
@ -69,10 +73,10 @@ pub trait Authority: Send {
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup>;
) -> Box<Future<Item = Self::Lookup, Error = LookupError> + Send>;
/// Get the NS, NameServer, record for the zone
fn ns(&self, is_secure: bool, supported_algorithms: SupportedAlgorithms) -> LookupResult<Self::Lookup> {
fn ns(&self, is_secure: bool, supported_algorithms: SupportedAlgorithms) -> Self::LookupFuture {
self.lookup(
self.origin(),
RecordType::NS,
@ -93,13 +97,13 @@ pub trait Authority: Send {
name: &LowerName,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup>;
) -> Self::LookupFuture;
/// Returns the SOA of the authority.
///
/// *Note*: This will only return the SOA, if this is fullfilling a request, a standard lookup
/// should be used, see `soa_secure()`, which will optionally return RRSIGs.
fn soa(&self) -> LookupResult<Self::Lookup> {
fn soa(&self) -> Self::LookupFuture {
// SOA should be origin|SOA
self.lookup(
self.origin(),
@ -114,7 +118,7 @@ pub trait Authority: Send {
&self,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
) -> Self::LookupFuture {
self.lookup(
self.origin(),
RecordType::SOA,

View File

@ -7,17 +7,17 @@
//! All authority related types
use futures::{future, Future, Poll};
use trust_dns::op::LowerQuery;
use trust_dns::proto::rr::dnssec::rdata::key::KEY;
use trust_dns::rr::dnssec::{DnsSecError, DnsSecResult, Signer, SupportedAlgorithms};
use trust_dns::rr::{LowerName, Name, RecordType};
use trust_dns::rr::{LowerName, Name, Record, RecordType};
use authority::result::LookupResult;
use authority::LookupObject;
use authority::{Authority, MessageRequest, UpdateResult, ZoneType};
use authority::{Authority, LookupError, MessageRequest, UpdateResult, ZoneType};
/// An Object safe Authority
pub trait AuthorityObject: Send {
pub trait AuthorityObject: Send + Sync {
/// What type is this zone
fn zone_type(&self) -> ZoneType;
@ -50,7 +50,7 @@ pub trait AuthorityObject: Send {
rtype: RecordType,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>>;
) -> BoxedLookupFuture;
/// Using the specified query, perform a lookup against this zone.
///
@ -68,14 +68,10 @@ pub trait AuthorityObject: Send {
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>>;
) -> BoxedLookupFuture;
/// Get the NS, NameServer, record for the zone
fn ns(
&self,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>> {
fn ns(&self, is_secure: bool, supported_algorithms: SupportedAlgorithms) -> BoxedLookupFuture {
self.lookup(
self.origin(),
RecordType::NS,
@ -96,13 +92,13 @@ pub trait AuthorityObject: Send {
name: &LowerName,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>>;
) -> BoxedLookupFuture;
/// Returns the SOA of the authority.
///
/// *Note*: This will only return the SOA, if this is fullfilling a request, a standard lookup
/// should be used, see `soa_secure()`, which will optionally return RRSIGs.
fn soa(&self) -> LookupResult<Box<dyn LookupObject>> {
fn soa(&self) -> BoxedLookupFuture {
// SOA should be origin|SOA
self.lookup(
self.origin(),
@ -117,7 +113,7 @@ pub trait AuthorityObject: Send {
&self,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>> {
) -> BoxedLookupFuture {
self.lookup(
self.origin(),
RecordType::SOA,
@ -151,8 +147,9 @@ pub trait AuthorityObject: Send {
impl<A, L> AuthorityObject for A
where
A: Authority<Lookup = L>,
L: LookupObject + 'static,
A: Authority<Lookup = L> + Send + Sync + 'static,
A::LookupFuture: Send + 'static,
L: LookupObject + Send + 'static,
{
/// What type is this zone
fn zone_type(&self) -> ZoneType {
@ -194,9 +191,9 @@ where
rtype: RecordType,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>> {
) -> BoxedLookupFuture {
let lookup = Authority::lookup(self, name, rtype, is_secure, supported_algorithms);
lookup.map(|l| Box::new(l) as Box<dyn LookupObject>)
BoxedLookupFuture::from(lookup.map(|l| Box::new(l) as Box<dyn LookupObject>))
}
/// Using the specified query, perform a lookup against this zone.
@ -215,9 +212,9 @@ where
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>> {
) -> BoxedLookupFuture {
let lookup = Authority::search(self, query, is_secure, supported_algorithms);
lookup.map(|l| Box::new(l) as Box<dyn LookupObject>)
BoxedLookupFuture::from(lookup.map(|l| Box::new(l) as Box<dyn LookupObject>))
}
/// Return the NSEC records based on the given name
@ -232,9 +229,9 @@ where
name: &LowerName,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Box<dyn LookupObject>> {
) -> BoxedLookupFuture {
let lookup = Authority::get_nsec_records(self, name, is_secure, supported_algorithms);
lookup.map(|l| Box::new(l) as Box<dyn LookupObject>)
BoxedLookupFuture::from(lookup.map(|l| Box::new(l) as Box<dyn LookupObject>))
}
fn add_update_auth_key(&mut self, name: Name, key: KEY) -> DnsSecResult<()> {
@ -249,3 +246,55 @@ where
Authority::secure_zone(self)
}
}
/// An Object Safe Lookup for Authority
pub trait LookupObject: Send {
/// Returns true if either the associated Records are empty, or this is a NameExists or NxDomain
fn is_empty(&self) -> bool;
/// Conversion to an iterator
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a>;
}
struct EmptyLookup;
impl LookupObject for EmptyLookup {
fn is_empty(&self) -> bool {
true
}
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a> {
Box::new([].iter())
}
}
/// A boxed lookup future
pub struct BoxedLookupFuture(
Box<dyn Future<Item = Box<dyn LookupObject>, Error = LookupError> + Send>,
);
impl BoxedLookupFuture {
/// Performs a conversion (boxes) into the future
pub fn from<T>(future: T) -> Self
where
T: Future<Item = Box<dyn LookupObject>, Error = LookupError> + Send + Sized + 'static,
{
BoxedLookupFuture(Box::new(future))
}
/// Creates an empty (i.e. no records) lookup future
pub fn empty() -> Self {
BoxedLookupFuture(Box::new(future::ok(
Box::new(EmptyLookup) as Box<dyn LookupObject>
)))
}
}
impl Future for BoxedLookupFuture {
type Item = Box<dyn LookupObject>;
type Error = LookupError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0.poll()
}
}

View File

@ -16,9 +16,12 @@
// TODO, I've implemented this as a seperate entity from the cache, but I wonder if the cache
// should be the only "front-end" for lookups, where if that misses, then we go to the catalog
// then, if requested, do a recursive lookup... i.e. the catalog would only point to files.
use std::borrow::Borrow;
use std::collections::HashMap;
use std::io;
use std::sync::RwLock;
use std::sync::{Arc, RwLock};
use futures::{Async, Future, Poll};
use server::{Request, RequestHandler, ResponseHandler};
use trust_dns::op::{Edns, Header, LowerQuery, MessageType, OpCode, ResponseCode};
@ -27,15 +30,15 @@ use trust_dns::rr::rdata::opt::{EdnsCode, EdnsOption};
use trust_dns::rr::{LowerName, RecordType};
use authority::{AuthLookup, MessageRequest, MessageResponse, MessageResponseBuilder, ZoneType};
use authority::{AuthorityObject, LookupError, LookupObject, LookupResult};
use authority::{AuthorityObject, BoxedLookupFuture, LookupError, LookupObject};
/// Set of authorities, zones, available to this server.
#[derive(Default)]
pub struct Catalog {
authorities: HashMap<LowerName, RwLock<Box<dyn AuthorityObject>>>,
authorities: HashMap<LowerName, Arc<RwLock<Box<dyn AuthorityObject>>>>,
}
fn send_response<R: ResponseHandler + 'static>(
fn send_response<R: ResponseHandler>(
response_edns: Option<Edns>,
mut response: MessageResponse,
response_handle: R,
@ -62,18 +65,20 @@ fn send_response<R: ResponseHandler + 'static>(
}
impl RequestHandler for Catalog {
type ResponseFuture = HandleRequest;
/// Determine's what needs to happen given the type of request, i.e. Query or Update.
///
/// # Arguments
///
/// * `request` - the requested action to perform.
/// * `response_handle` - sink for the response message to be sent
fn handle_request<'q, 'a, R: ResponseHandler + 'static>(
&'a self,
request: &'q Request,
fn handle_request<R: ResponseHandler>(
&self,
request: Request,
response_handle: R,
) -> io::Result<()> {
let request_message = &request.message;
) -> Self::ResponseFuture {
let request_message = request.message;
trace!("request: {:?}", request_message);
let response_edns: Option<Edns>;
@ -103,7 +108,9 @@ impl RequestHandler for Catalog {
response.edns(resp_edns);
// TODO: should ResponseHandle consume self?
return response_handle.send_response(response.build_no_records(response_header));
let result =
response_handle.send_response(response.build_no_records(response_header));
return HandleRequest::result(result);
}
response_edns = Some(resp_edns);
@ -115,16 +122,26 @@ impl RequestHandler for Catalog {
// TODO think about threading query lookups for multiple lookups, this could be a huge improvement
// especially for recursive lookups
MessageType::Query => match request_message.op_code() {
OpCode::Query => self.lookup(request_message, response_edns, response_handle),
OpCode::Update => self.update(request_message, response_edns, response_handle),
OpCode::Query => {
debug!("query received: {}", request_message.id());
let lookup = self.lookup(request_message, response_edns, response_handle);
HandleRequest::lookup(lookup)
}
OpCode::Update => {
debug!("update received: {}", request_message.id());
// TODO: this should be a future
let result = self.update(&request_message, response_edns, response_handle);
HandleRequest::result(result)
}
c => {
error!("unimplemented op_code: {:?}", c);
warn!("unimplemented op_code: {:?}", c);
let response = MessageResponseBuilder::new(Some(request_message.raw_queries()));
response_handle.send_response(response.error_msg(
let result = response_handle.send_response(response.error_msg(
request_message.id(),
request_message.op_code(),
ResponseCode::NotImp,
))
));
HandleRequest::result(result)
}
},
MessageType::Response => {
@ -134,11 +151,46 @@ impl RequestHandler for Catalog {
);
let response = MessageResponseBuilder::new(Some(request_message.raw_queries()));
response_handle.send_response(response.error_msg(
let result = response_handle.send_response(response.error_msg(
request_message.id(),
request_message.op_code(),
ResponseCode::FormErr,
))
));
HandleRequest::result(result)
}
}
}
}
/// Future response to handle a request
#[must_use = "futures do nothing unless polled"]
pub enum HandleRequest {
LookupFuture(Box<dyn Future<Item = (), Error = ()> + Send>),
Result(io::Result<()>),
}
impl HandleRequest {
fn lookup<R: ResponseHandler>(lookup_future: LookupFuture<R>) -> Self {
let lookup = Box::new(lookup_future) as Box<dyn Future<Item = (), Error = ()> + Send>;
HandleRequest::LookupFuture(lookup)
}
fn result(result: io::Result<()>) -> Self {
HandleRequest::Result(result)
}
}
impl Future for HandleRequest {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self {
HandleRequest::LookupFuture(lookup) => lookup.poll(),
HandleRequest::Result(Ok(_)) => Ok(Async::Ready(())),
HandleRequest::Result(Err(res)) => {
error!("update failed: {}", res);
Ok(Async::Ready(()))
}
}
}
@ -159,11 +211,13 @@ impl Catalog {
/// * `name` - zone name, e.g. example.com.
/// * `authority` - the zone data
pub fn upsert(&mut self, name: LowerName, authority: Box<dyn AuthorityObject>) {
self.authorities.insert(name, RwLock::new(authority));
// self.authorities.insert(name, Arc::new(RwLock::new(authority)));
self.authorities
.insert(name, Arc::new(RwLock::new(authority)));
}
/// Remove a zone from the catalog
pub fn remove(&mut self, name: &LowerName) -> Option<RwLock<Box<dyn AuthorityObject>>> {
pub fn remove(&mut self, name: &LowerName) -> Option<Arc<RwLock<Box<dyn AuthorityObject>>>> {
self.authorities.remove(name)
}
@ -330,143 +384,59 @@ impl Catalog {
///
/// * `request` - the query message.
/// * `response_handle` - sink for the response message to be sent
pub fn lookup<'q, R: ResponseHandler + 'static>(
pub fn lookup<R: ResponseHandler>(
&self,
request: &'q MessageRequest,
request: MessageRequest,
response_edns: Option<Edns>,
response_handle: R,
) -> io::Result<()> {
) -> LookupFuture<R> {
let request = Arc::new(request);
let response_edns = response_edns.map(Arc::new);
// TODO: the spec is very unclear on what to do with multiple queries
// we will search for each, in the future, maybe make this threaded to respond even faster.
for query in request.queries() {
if let Some(ref_authority) = self.find(query.name()) {
let authority = &ref_authority.read().unwrap(); // poison errors should panic
info!(
"request: {} found authority: {}",
request.id(),
authority.origin()
);
// the current impl will return on the first query result
let mut response = MessageResponseBuilder::new(Some(request.raw_queries()));
let mut response_header = Header::new();
response_header.set_id(request.id());
response_header.set_op_code(OpCode::Query);
response_header.set_message_type(MessageType::Response);
response_header.set_authoritative(match authority.zone_type() {
ZoneType::Master | ZoneType::Slave => true,
_ => false,
});
// collect all the queries and lookups
let queries_and_authorities = request
.queries()
.iter()
.enumerate()
.filter_map(|(idx, q)| {
self.find(q.name())
.map(|authority| (idx, Arc::clone(authority)))
})
.collect::<Vec<_>>();
let (is_dnssec, supported_algorithms) =
request
.edns()
.map_or((false, SupportedAlgorithms::new()), |edns| {
let supported_algorithms =
if let Some(&EdnsOption::DAU(algs)) = edns.option(EdnsCode::DAU) {
algs
} else {
debug!("no DAU in request, used default SupportAlgorithms");
Default::default()
};
let found_authority = !queries_and_authorities.is_empty();
(edns.dnssec_ok(), supported_algorithms)
});
// log algorithms being requested
if is_dnssec {
info!(
"request: {} supported_algs: {}",
request.id(),
supported_algorithms
);
}
let records: LookupResult<Box<dyn LookupObject>> =
authority.search(query, is_dnssec, supported_algorithms);
// setup headers
// and add records
match records {
Ok(records) => {
response_header.set_response_code(ResponseCode::NoError);
response_header.set_authoritative(true);
// get the NS records
// FIXME: just ignore NS errors?
let ns = authority.ns(is_dnssec, supported_algorithms).unwrap_or_else(|_| Box::new(AuthLookup::default()) as Box<dyn LookupObject>);
let soa = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
return send_response(
response_edns,
response.build(response_header, records.iter(), ns.iter(), soa.iter()),
response_handle,
);
}
Err(LookupError::ResponseCode(ResponseCode::Refused)) => {
response_header.set_response_code(ResponseCode::Refused);
let ns = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
let soa = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
let records = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
return send_response(
response_edns,
response.build(response_header, records.iter(), ns.iter(), soa.iter()),
response_handle,
);
}
Err(e) => {
// in the not found case it's standard to return the SOA in the authority section
// if the name is in this zone, etc.
// see https://tools.ietf.org/html/rfc2308 for proper response construct
if e.is_nx_domain() {
response_header.set_response_code(ResponseCode::NXDomain);
} else if e.is_name_exists() {
response_header.set_response_code(ResponseCode::NoError);
};
// in the dnssec case, nsec records should exist, we return NoError + NoData + NSec...
let ns: Box<dyn LookupObject> = if is_dnssec {
// get NSEC records
let mut nsecs = authority.get_nsec_records(
query.name(),
is_dnssec,
supported_algorithms,
).unwrap_or_else(|_| Box::new(AuthLookup::default()) as Box<dyn LookupObject>);
debug!("request: {} non-existent adding nsecs", request.id(),);
nsecs
} else {
// place holder...
debug!("request: {} non-existent", request.id());
Box::new(AuthLookup::default()) as Box<dyn LookupObject>
};
let soa = authority.soa_secure(is_dnssec, supported_algorithms).unwrap_or_else(|_| Box::new(AuthLookup::default()) as Box<dyn LookupObject>);
let records = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
return send_response(
response_edns,
response.build(response_header, records.iter(), ns.iter(), soa.iter()),
response_handle,
);
}
}
}
if !found_authority {
let response = MessageResponseBuilder::new(Some(request.raw_queries()));
send_response(
response_edns
.as_ref()
.map(|arc| Borrow::<Edns>::borrow(arc).clone()),
response.error_msg(request.id(), request.op_code(), ResponseCode::NXDomain),
response_handle.clone(),
)
.map_err(|e| error!("failed to send response: {}", e))
.ok();
}
let response = MessageResponseBuilder::new(Some(request.raw_queries()));
send_response(
LookupFuture::new(
request,
response_edns,
response.error_msg(request.id(), request.op_code(), ResponseCode::NXDomain),
response_handle,
queries_and_authorities,
)
}
/// Recursively searches the catalog for a matching authority
pub fn find(&self, name: &LowerName) -> Option<&RwLock<Box<dyn AuthorityObject>>> {
pub fn find(&self, name: &LowerName) -> Option<&Arc<RwLock<Box<dyn AuthorityObject>>>> {
debug!("searching authorities for: {}", name);
self.authorities.get(name).or_else(|| {
let name = name.base_name();
if !name.is_root() {
let name = name.base_name();
self.find(&name)
} else {
None
@ -474,3 +444,520 @@ impl Catalog {
})
}
}
/// A Future that runs the lookups and responds to the requests
#[allow(clippy::type_complexity)]
#[must_use = "futures do nothing unless polled"]
pub struct LookupFuture<R: ResponseHandler> {
request: Arc<MessageRequest>,
response_edns: Option<Arc<Edns>>,
response_handle: R,
queries_and_authorities: Vec<(usize, Arc<RwLock<Box<dyn AuthorityObject>>>)>,
lookup: Option<AuthorityLookup<R>>,
}
impl<R: ResponseHandler> LookupFuture<R> {
#[allow(clippy::type_complexity)]
fn new(
request: Arc<MessageRequest>,
response_edns: Option<Arc<Edns>>,
response_handle: R,
queries_and_authorities: Vec<(usize, Arc<RwLock<Box<dyn AuthorityObject>>>)>,
) -> Self {
LookupFuture {
request,
response_edns,
response_handle,
queries_and_authorities,
lookup: None,
}
}
}
impl<R: ResponseHandler> Future for LookupFuture<R> {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
// See if the lookup had anything
match self.lookup.as_mut().map(|l| l.poll()) {
Some(Ok(Async::NotReady)) => return Ok(Async::NotReady),
Some(Ok(Async::Ready(()))) => (),
Some(Err(())) => error!("unexpected error result in LookupFuture"),
None => (),
}
// lookup is complete, make sure it is None at this point
self.lookup = None;
// get next query
let (query_idx, ref_authority) = if let Some(q_a) = self.queries_and_authorities.pop() {
q_a
} else {
// all lookups are complete, finish request
return Ok(Async::Ready(()));
};
let query = if let Some(query) = self.request.queries().get(query_idx) {
query
} else {
// bad state, could just panic
error!("query_idx out of bounds? {}", query_idx);
continue;
};
let authority = &ref_authority.read().unwrap(); // poison errors should panic
info!(
"request: {} found authority: {}",
self.request.id(),
authority.origin()
);
let mut response_header = Header::new();
response_header.set_id(self.request.id());
response_header.set_op_code(OpCode::Query);
response_header.set_message_type(MessageType::Response);
response_header.set_authoritative(authority.zone_type().is_authoritative());
let (is_dnssec, supported_algorithms) =
self.request
.edns()
.map_or((false, SupportedAlgorithms::new()), |edns| {
let supported_algorithms =
if let Some(&EdnsOption::DAU(algs)) = edns.option(EdnsCode::DAU) {
algs
} else {
debug!("no DAU in request, used default SupportAlgorithms");
Default::default()
};
(edns.dnssec_ok(), supported_algorithms)
});
// log algorithms being requested
if is_dnssec {
info!(
"request: {} supported_algs: {}",
self.request.id(),
supported_algorithms
);
}
debug!("performing {} on {}", query, authority.origin());
let lookup_future = authority.search(query, is_dnssec, supported_algorithms);
let request_params = RequestParams {
is_dnssec,
supported_algorithms,
query: query.clone(),
request: Arc::clone(&self.request),
};
let response_params = ResponseParams {
response_edns: self.response_edns.clone(),
response_header,
response_handle: self.response_handle.clone(),
};
match authority.zone_type() {
ZoneType::Master | ZoneType::Slave => {
self.lookup = Some(AuthorityLookup::authority(
self.request.id(),
response_params,
request_params,
lookup_future,
Arc::clone(&ref_authority),
));
}
ZoneType::Forward | ZoneType::Hint => {
self.lookup = Some(AuthorityLookup::resolve(
self.request.id(),
response_params,
request_params,
lookup_future,
Arc::clone(&ref_authority),
));
}
}
}
}
}
struct RequestParams {
is_dnssec: bool,
supported_algorithms: SupportedAlgorithms,
query: LowerQuery,
request: Arc<MessageRequest>,
}
struct ResponseParams<R: ResponseHandler> {
response_edns: Option<Arc<Edns>>,
response_header: Header,
response_handle: R,
}
/// Authority Lookup future to perform all actions for authoritative responses.
#[must_use = "futures do nothing unless polled"]
struct AuthorityLookup<R: ResponseHandler> {
response_params: Option<ResponseParams<R>>,
request_params: RequestParams,
authority: Arc<RwLock<Box<dyn AuthorityObject>>>,
state: AuthOrResolve,
}
impl<R: ResponseHandler> AuthorityLookup<R> {
fn authority(
request_id: u16,
response_params: ResponseParams<R>,
request_params: RequestParams,
record_lookup: BoxedLookupFuture,
authority: Arc<RwLock<Box<dyn AuthorityObject>>>,
) -> Self {
debug!("handling authoritative request: {}", request_id);
AuthorityLookup {
response_params: Some(response_params),
request_params,
authority,
state: AuthOrResolve::AuthorityLookupState(AuthorityLookupState::Records {
record_lookup,
}),
}
}
fn resolve(
request_id: u16,
response_params: ResponseParams<R>,
request_params: RequestParams,
record_lookup: BoxedLookupFuture,
authority: Arc<RwLock<Box<dyn AuthorityObject>>>,
) -> Self {
debug!("handling forwarded resolve: {}", request_id);
AuthorityLookup {
response_params: Some(response_params),
request_params,
authority,
state: AuthOrResolve::ResolveLookupState(ResolveLookupState::Records { record_lookup }),
}
}
}
impl<R: ResponseHandler> Future for AuthorityLookup<R> {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let (records, soa, ns) = try_ready!(self.state.poll(
&self.request_params,
self.response_params
.as_mut()
.expect("bad state, response_params should not be none here"),
&self.authority
));
let response_params = self
.response_params
.take()
.expect("AuthorityLookup already complete");
let response_edns = response_params.response_edns;
let response = MessageResponseBuilder::new(Some(self.request_params.request.raw_queries()));
let response_header = response_params.response_header;
let response_handle = response_params.response_handle;
send_response(
response_edns
.as_ref()
.map(|arc| Borrow::<Edns>::borrow(arc).clone()),
response.build(response_header, records.iter(), ns.iter(), soa.iter()),
response_handle,
)
.map_err(|e| error!("error sending response: {}", e))
.ok();
Ok(Async::Ready(()))
}
}
#[must_use = "futures do nothing unless polled"]
enum AuthOrResolve {
AuthorityLookupState(AuthorityLookupState),
ResolveLookupState(ResolveLookupState),
}
impl AuthOrResolve {
#[allow(clippy::type_complexity)]
fn poll<R: ResponseHandler>(
&mut self,
request_params: &RequestParams,
response_params: &mut ResponseParams<R>,
authority: &Arc<RwLock<Box<dyn AuthorityObject>>>,
) -> Poll<
(
Box<dyn LookupObject>,
Box<dyn LookupObject>,
Box<dyn LookupObject>,
),
(),
> {
match self {
AuthOrResolve::AuthorityLookupState(a) => {
a.poll(request_params, response_params, authority)
}
AuthOrResolve::ResolveLookupState(r) => {
r.poll(request_params, response_params, authority)
}
}
}
}
/// state machine for handling the response to the request
#[must_use = "futures do nothing unless polled"]
enum AuthorityLookupState {
/// This is the initial lookup for the Records
Records { record_lookup: BoxedLookupFuture },
/// This performs the lookup for NS records, transitions to complete after
LookupNs {
ns_lookup: BoxedLookupFuture,
records: Option<Box<dyn LookupObject>>,
},
/// In a negative response case, we need to return the NSEC records
NxLookupNsec { nsec_lookup: BoxedLookupFuture },
/// In a negative response case, we need to return the NSEC records
NxLookupSoa {
soa_lookup: BoxedLookupFuture,
nsecs: Option<Box<dyn LookupObject>>,
},
/// The Completion state
Complete {
// TODO: convert to a single Option with all Boxes
records: Option<Box<dyn LookupObject>>,
soa: Option<Box<dyn LookupObject>>,
ns: Option<Box<dyn LookupObject>>,
},
}
impl AuthorityLookupState {
#[allow(clippy::type_complexity)]
fn poll<R: ResponseHandler>(
&mut self,
request_params: &RequestParams,
response_params: &mut ResponseParams<R>,
authority: &Arc<RwLock<Box<dyn AuthorityObject>>>,
) -> Poll<
(
Box<dyn LookupObject>,
Box<dyn LookupObject>,
Box<dyn LookupObject>,
),
(),
> {
loop {
*self = match self {
// In this state we await the records, on success we transition to getting
// NS records, which indicate an authoritative response.
//
// On Errors, the transition depends on the type of error.
AuthorityLookupState::Records { record_lookup } => {
match record_lookup.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(records)) => {
response_params
.response_header
.set_response_code(ResponseCode::NoError);
response_params.response_header.set_authoritative(true);
// This was a successful authoritative lookup:
// get the NS records
let ns_lookup = authority.read().expect("authority poisoned").ns(
request_params.is_dnssec,
request_params.supported_algorithms,
);
AuthorityLookupState::LookupNs {
ns_lookup,
records: Some(records),
}
}
// This request was refused
// TODO: there are probably other error cases that should just drop through (FormErr, ServFail)
Err(LookupError::ResponseCode(ResponseCode::Refused)) => {
response_params
.response_header
.set_response_code(ResponseCode::Refused);
let ns = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
let soa = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
let records = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
AuthorityLookupState::Complete {
records: Some(records),
soa: Some(soa),
ns: Some(ns),
}
}
// in the not found case it's standard to return the SOA in the authority section
// if the name is in this zone, etc.
// see https://tools.ietf.org/html/rfc2308 for proper response construct
Err(e) => {
if e.is_nx_domain() {
response_params
.response_header
.set_response_code(ResponseCode::NXDomain);
} else if e.is_name_exists() {
response_params
.response_header
.set_response_code(ResponseCode::NoError);
};
// in the dnssec case, nsec records should exist, we return NoError + NoData + NSec...
let nsec_lookup = if request_params.is_dnssec {
debug!(
"request: {} non-existent adding nsecs",
request_params.request.id()
);
dbg!("getting nsecs");
// get NSEC records
let nsecs = authority
.read()
.expect("authority poisoned")
.get_nsec_records(
request_params.query.name(),
true,
request_params.supported_algorithms,
);
BoxedLookupFuture::from(nsecs)
} else {
// place holder...
debug!("request: {} non-existent", request_params.request.id());
BoxedLookupFuture::empty()
};
AuthorityLookupState::NxLookupNsec { nsec_lookup }
}
}
}
// This is still a successful path, getting the ns records,
// which represent an Authoritative answer
AuthorityLookupState::LookupNs { ns_lookup, records } => {
// ns is allowed to fail
let ns = match ns_lookup.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(ns)) => ns,
Err(e) => {
warn!("ns_lookup errored: {}", e);
Box::new(AuthLookup::default()) as Box<dyn LookupObject>
}
};
let soa = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
AuthorityLookupState::Complete {
records: records.take(),
soa: Some(soa),
ns: Some(ns),
}
}
// run the nsec lookup future, and then transition to get soa
AuthorityLookupState::NxLookupNsec { nsec_lookup } => {
let nsecs = match nsec_lookup.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(nsecs)) => nsecs,
Err(e) => {
warn!("failed to lookup nsecs: {}", e);
Box::new(AuthLookup::default()) as Box<dyn LookupObject>
}
};
let soa_lookup = authority.read().expect("authority poisoned").soa_secure(
request_params.is_dnssec,
request_params.supported_algorithms,
);
AuthorityLookupState::NxLookupSoa {
soa_lookup,
nsecs: Some(nsecs),
}
}
// run the soa lookup, and then transition to complete
AuthorityLookupState::NxLookupSoa { soa_lookup, nsecs } => {
let soa = match soa_lookup.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(soa)) => soa,
Err(e) => {
warn!("failed to lookup soa: {}", e);
Box::new(AuthLookup::default()) as Box<dyn LookupObject>
}
};
let records = Box::new(AuthLookup::default()) as Box<dyn LookupObject>;
AuthorityLookupState::Complete {
records: Some(records),
soa: Some(soa),
ns: nsecs.take(),
}
}
// everything is done, return results.
AuthorityLookupState::Complete { records, soa, ns } => {
return Ok(Async::Ready((
records
.take()
.expect("AuthorityLookupState already complete"),
soa.take().expect("AuthorityLookupState already complete"),
ns.take().expect("AuthorityLookupState already complete"),
)));
}
}
}
}
}
/// state machine for handling the response to the request
#[must_use = "futures do nothing unless polled"]
enum ResolveLookupState {
/// This is the initial lookup for the Records
Records { record_lookup: BoxedLookupFuture },
}
impl ResolveLookupState {
#[allow(clippy::type_complexity)]
fn poll<R: ResponseHandler>(
&mut self,
_request_params: &RequestParams,
response_params: &mut ResponseParams<R>,
_authority: &Arc<RwLock<Box<dyn AuthorityObject>>>,
) -> Poll<
(
Box<dyn LookupObject>,
Box<dyn LookupObject>,
Box<dyn LookupObject>,
),
(),
> {
#[allow(clippy::never_loop)]
loop {
// FIXME: way more states to consider.
/* *self = */
match self {
// In this state we await the records, on success we transition to getting
// NS records, which indicate an authoritative response.
//
// On Errors, the transition depends on the type of error.
ResolveLookupState::Records { record_lookup } => {
let records = try_ready!(record_lookup
.poll()
.map_err(|e| error!("error resolving: {}", e)));
// need to clone the result codes...
response_params.response_header.set_authoritative(false);
return Ok(Async::Ready((
records,
Box::new(AuthLookup::default()) as Box<dyn LookupObject>,
Box::new(AuthLookup::default()) as Box<dyn LookupObject>,
)));
}
}
}
}
}

View File

@ -5,10 +5,15 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::error;
use std::fmt;
use std::io;
use trust_dns::op::ResponseCode;
#[cfg(feature = "trust-dns-resolver")]
use trust_dns_resolver::error::ResolveError;
// TODO: should this implement Failure?
/// A query could not be fullfilled
#[derive(Debug, Eq, PartialEq)]
pub enum LookupError {
@ -16,6 +21,11 @@ pub enum LookupError {
NameExists,
/// There was an error performing the lookup
ResponseCode(ResponseCode),
/// Resolve Error
#[cfg(feature = "trust-dns-resolver")]
ResolveError(String), /* TODO: what to do here? */
/// An underlying IO error occured
Io(String), /* TODO: what to do here? */
}
impl LookupError {
@ -53,11 +63,17 @@ impl fmt::Display for LookupError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LookupError::NameExists => write!(f, "NameExists"),
LookupError::ResponseCode(rc) => write!(f, "{}", rc),
LookupError::ResponseCode(rc) => write!(f, "response_code: {}", rc),
#[cfg(feature = "trust-dns-resolver")]
LookupError::ResolveError(e) => write!(f, "resolve_error: {}", e),
LookupError::Io(e) => write!(f, "io: {}", e),
}
}
}
// FIXME: better error impls
impl error::Error for LookupError {}
impl From<ResponseCode> for LookupError {
fn from(code: ResponseCode) -> Self {
// this should never be a NoError
@ -66,4 +82,24 @@ impl From<ResponseCode> for LookupError {
}
}
#[cfg(feature = "trust-dns-resolver")]
impl From<ResolveError> for LookupError {
fn from(e: ResolveError) -> Self {
LookupError::ResolveError(e.to_string())
}
}
impl From<io::Error> for LookupError {
fn from(e: io::Error) -> Self {
LookupError::Io(e.to_string())
}
}
impl From<LookupError> for io::Error {
fn from(e: LookupError) -> Self {
io::Error::new(io::ErrorKind::Other, Box::new(e))
}
}
/// Result of a Lookup in the Catalog and Authority
pub type LookupResult<T> = Result<T, LookupError>;

View File

@ -1,10 +0,0 @@
use proto::rr::Record;
/// An Object Safe Lookup for Authority
pub trait LookupObject {
/// Returns true if either the associated Records are empty, or this is a NameExists or NxDomain
fn is_empty(&self) -> bool;
/// Conversion to an iterator
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Record> + Send + 'a>;
}

View File

@ -14,9 +14,9 @@ use trust_dns::op::LowerQuery;
/// A Message which captures the data from an inbound request
#[derive(Debug, PartialEq)]
pub struct MessageRequest<'q> {
pub struct MessageRequest {
header: Header,
queries: Queries<'q>,
queries: Queries,
answers: Vec<Record>,
name_servers: Vec<Record>,
additionals: Vec<Record>,
@ -24,7 +24,7 @@ pub struct MessageRequest<'q> {
edns: Option<Edns>,
}
impl<'q> MessageRequest<'q> {
impl MessageRequest {
/// see `Header::id()`
pub fn id(&self) -> u16 {
self.header.id()
@ -175,7 +175,7 @@ impl<'q> MessageRequest<'q> {
}
}
impl<'q> BinDecodable<'q> for MessageRequest<'q> {
impl<'q> BinDecodable<'q> for MessageRequest {
// TODO: generify this with Message?
/// Reads a MessageRequest from the decoder
fn read(decoder: &mut BinDecoder<'q>) -> ProtoResult<Self> {
@ -211,13 +211,13 @@ impl<'q> BinDecodable<'q> for MessageRequest<'q> {
/// A set of Queries with the associated serialized data
#[derive(Debug, PartialEq)]
pub struct Queries<'q> {
pub struct Queries {
queries: Vec<LowerQuery>,
original: &'q [u8],
original: Box<[u8]>,
}
impl<'q> Queries<'q> {
fn read_queries(decoder: &mut BinDecoder<'q>, count: usize) -> ProtoResult<Vec<LowerQuery>> {
impl Queries {
fn read_queries(decoder: &mut BinDecoder, count: usize) -> ProtoResult<Vec<LowerQuery>> {
let mut queries = Vec::with_capacity(count);
for _ in 0..count {
queries.push(LowerQuery::read(decoder)?);
@ -226,10 +226,13 @@ impl<'q> Queries<'q> {
}
/// Read queries from a decoder
pub fn read(decoder: &mut BinDecoder<'q>, num_queries: usize) -> ProtoResult<Self> {
pub fn read(decoder: &mut BinDecoder, num_queries: usize) -> ProtoResult<Self> {
let queries_start = decoder.index();
let queries = Self::read_queries(decoder, num_queries)?;
let original = decoder.slice_from(queries_start)?;
let original = decoder
.slice_from(queries_start)?
.to_vec()
.into_boxed_slice();
Ok(Queries { queries, original })
}
@ -246,13 +249,13 @@ impl<'q> Queries<'q> {
/// returns the bytes as they were seen from the Client
pub fn as_bytes(&self) -> &[u8] {
self.original
self.original.as_ref()
}
pub(crate) fn as_emit_and_count(&self) -> QueriesEmitAndCount {
QueriesEmitAndCount {
length: self.queries.len(),
original: self.original,
original: self.original.as_ref(),
}
}
}
@ -269,7 +272,7 @@ impl<'q> EmitAndCount for QueriesEmitAndCount<'q> {
}
}
impl<'q> BinEncodable for MessageRequest<'q> {
impl BinEncodable for MessageRequest {
fn emit(&self, encoder: &mut BinEncoder) -> ProtoResult<()> {
message::emit_message_parts(
&self.header,
@ -307,7 +310,7 @@ pub trait UpdateRequest {
fn sig0(&self) -> &[Record];
}
impl<'q> UpdateRequest for MessageRequest<'q> {
impl UpdateRequest for MessageRequest {
fn id(&self) -> u16 {
MessageRequest::id(self)
}

View File

@ -28,7 +28,7 @@ pub struct MessageResponse<
S: Iterator<Item = &'a Record> + Send + 'a,
{
header: Header,
queries: Option<&'q Queries<'q>>,
queries: Option<&'q Queries>,
answers: A,
name_servers: N,
soa: S,
@ -42,8 +42,8 @@ enum EmptyOrQueries<'q> {
Queries(QueriesEmitAndCount<'q>),
}
impl<'q> From<Option<&'q Queries<'q>>> for EmptyOrQueries<'q> {
fn from(option: Option<&'q Queries<'q>>) -> Self {
impl<'q> From<Option<&'q Queries>> for EmptyOrQueries<'q> {
fn from(option: Option<&'q Queries>) -> Self {
option.map_or(EmptyOrQueries::Empty, |q| {
EmptyOrQueries::Queries(q.as_emit_and_count())
})
@ -96,7 +96,7 @@ where
/// A builder for MessageResponses
pub struct MessageResponseBuilder<'q> {
queries: Option<&'q Queries<'q>>,
queries: Option<&'q Queries>,
sig0: Option<Vec<Record>>,
edns: Option<Edns>,
}
@ -107,7 +107,7 @@ impl<'q> MessageResponseBuilder<'q> {
/// # Arguments
///
/// * `queries` - any optional set of Queries to associate with the Response
pub fn new(queries: Option<&'q Queries<'q>>) -> MessageResponseBuilder<'q> {
pub fn new(queries: Option<&'q Queries>) -> MessageResponseBuilder<'q> {
MessageResponseBuilder {
queries,
sig0: None,
@ -154,7 +154,7 @@ impl<'q> MessageResponseBuilder<'q> {
}
/// Construct a Response with no associated records
pub fn build_no_records(self, header: Header) -> MessageResponse<'q, 'q> {
pub fn build_no_records(self, header: Header) -> MessageResponse<'q, 'static> {
MessageResponse {
header,
queries: self.queries,
@ -179,7 +179,7 @@ impl<'q> MessageResponseBuilder<'q> {
id: u16,
op_code: OpCode,
response_code: ResponseCode,
) -> MessageResponse<'q, 'q> {
) -> MessageResponse<'q, 'static> {
let mut header = Header::default();
header.set_message_type(MessageType::Response);
header.set_id(id);

View File

@ -12,36 +12,23 @@ use trust_dns::op::ResponseCode;
/// Result of an Update operation
pub type UpdateResult<T> = Result<T, ResponseCode>;
/// The type of zone stored in a Catalog
#[derive(Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
pub enum ZoneType {
/// This authority for a zone, i.e. the Primary
Master,
/// A secondary, i.e. replicated from the Master
Slave,
/// A cached zone with recursive resolver abilities
Hint,
/// A cached zone where all requests are forwarded to another Resolver
Forward,
}
mod auth_lookup;
#[allow(clippy::module_inception)]
mod authority;
pub(crate) mod authority_object;
mod catalog;
pub(crate) mod lookup_object;
mod error;
pub(crate) mod message_request;
mod message_response;
mod result;
mod zone_type;
pub use self::auth_lookup::{
AnyRecords, AuthLookup, AuthLookupIter, LookupRecords, LookupRecordsIter,
};
pub use self::authority::Authority;
pub use self::authority_object::AuthorityObject;
pub use self::authority_object::{AuthorityObject, BoxedLookupFuture, LookupObject};
pub use self::catalog::Catalog;
pub use self::lookup_object::LookupObject;
pub use self::error::{LookupError, LookupResult};
pub use self::message_request::{MessageRequest, Queries, UpdateRequest};
pub use self::message_response::{MessageResponse, MessageResponseBuilder};
pub use self::result::{LookupError, LookupResult};
pub use self::zone_type::ZoneType;

View File

@ -0,0 +1,29 @@
// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
/// The type of zone stored in a Catalog
#[derive(Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
pub enum ZoneType {
/// This authority for a zone, i.e. the Primary
Master,
/// A secondary, i.e. replicated from the Master
Slave,
/// A cached zone with recursive resolver abilities
Hint,
/// A cached zone where all requests are forwarded to another Resolver
Forward,
}
impl ZoneType {
/// Is this an authoritative Authority, i.e. it owns the records of the zone.
pub fn is_authoritative(self) -> bool {
match self {
ZoneType::Master | ZoneType::Slave => true,
_ => false,
}
}
}

View File

@ -31,6 +31,7 @@ extern crate bytes;
extern crate chrono;
extern crate env_logger;
extern crate failure;
#[macro_use]
extern crate futures;
#[macro_use]
extern crate log;

View File

@ -50,7 +50,7 @@ fn get_env() -> String {
fn all_trust_dns(level: &str) -> String {
format!(
",named={level},trust_dns_server={level},trust_dns_proto={level}",
",named={level},trust_dns={level},trust_dns_server={level},trust_dns_proto={level},trust_dns_resolver={level}",
level = level
)
}

View File

@ -21,6 +21,7 @@
//! -p PORT, --port=PORT Override the listening port
//! --tls-port=PORT Override the listening port for TLS connections
//! ```
#![recursion_limit = "128"]
extern crate chrono;
#[macro_use]
@ -31,6 +32,7 @@ extern crate log;
#[cfg(feature = "dns-over-rustls")]
extern crate rustls;
extern crate tokio;
extern crate tokio_executor;
extern crate tokio_tcp;
extern crate tokio_udp;
extern crate trust_dns;
@ -42,24 +44,26 @@ extern crate trust_dns_server;
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs};
use std::path::Path;
use std::path::{Path, PathBuf};
use clap::{Arg, ArgMatches};
use futures::{future, Future};
use tokio::runtime::current_thread::Runtime;
use tokio::runtime::Runtime;
use tokio::runtime::TaskExecutor;
use tokio_tcp::TcpListener;
use tokio_udp::UdpSocket;
use trust_dns::rr::Name;
#[cfg(feature = "dnssec")]
use trust_dns::rr::rdata::key::KeyUsage;
use trust_dns_server::authority::{Authority, AuthorityObject, Catalog, ZoneType};
use trust_dns_server::config::{Config, ZoneConfig};
use trust_dns::rr::Name;
use trust_dns_server::authority::{AuthorityObject, Catalog, ZoneType};
#[cfg(any(feature = "dns-over-tls", feature = "dnssec"))]
use trust_dns_server::config::dnssec::{self, TlsCertConfig};
use trust_dns_server::config::{Config, ZoneConfig};
use trust_dns_server::logger;
use trust_dns_server::server::ServerFuture;
use trust_dns_server::store::file::{FileAuthority, FileConfig};
#[cfg(feature = "trust-dns-resolver")]
use trust_dns_server::store::forwarder::ForwardAuthority;
use trust_dns_server::store::sqlite::{SqliteAuthority, SqliteConfig};
use trust_dns_server::store::StoreConfig;
@ -68,6 +72,7 @@ use trust_dns_server::store::StoreConfig;
fn load_zone(
zone_dir: &Path,
zone_config: &ZoneConfig,
executor: &TaskExecutor,
) -> Result<Box<dyn AuthorityObject>, String> {
use std::path::PathBuf;
@ -114,6 +119,18 @@ fn load_zone(
)
.map(Box::new)?
}
#[cfg(feature = "trust-dns-resolver")]
Some(StoreConfig::Forward(ref config)) => {
use futures::future::Executor;
let (forwarder, bg) = ForwardAuthority::try_from_config(zone_name, zone_type, config)?;
executor
.execute(bg)
.expect("failed to background forwarder");
Box::new(forwarder)
}
None if zone_config.is_update_allowed() => {
warn!(
"using deprecated SQLite load configuration, please move to [[zones.stores]] form"
@ -173,17 +190,19 @@ fn load_zone(
key_config.is_zone_update_auth()
);
if key_config.is_zone_signing_key() {
let zone_signer = key_config.try_into_signer(zone_name.clone()).map_err(|e| {
format!("failed to load key: {:?} msg: {}", key_config.key_path(), e)
})?;
let zone_signer =
key_config.try_into_signer(zone_name.clone()).map_err(|e| {
format!("failed to load key: {:?} msg: {}", key_config.key_path(), e)
})?;
authority
.add_zone_signing_key(zone_signer)
.expect("failed to add zone signing key to authority");
}
if key_config.is_zone_update_auth() {
let update_auth_signer = key_config.try_into_signer(zone_name.clone()).map_err(|e| {
format!("failed to load key: {:?} msg: {}", key_config.key_path(), e)
})?;
let update_auth_signer =
key_config.try_into_signer(zone_name.clone()).map_err(|e| {
format!("failed to load key: {:?} msg: {}", key_config.key_path(), e)
})?;
let public_key = update_auth_signer
.key()
.to_sig0key_with_usage(update_auth_signer.algorithm(), KeyUsage::Host)
@ -340,11 +359,13 @@ pub fn main() {
.unwrap_or_else(|e| panic!("could not read config {}: {:?}", config_path.display(), e));
let directory_config = config.get_directory().to_path_buf();
let flag_zonedir = args.flag_zonedir.clone();
let zone_dir: &Path = flag_zonedir
let zone_dir: PathBuf = flag_zonedir
.as_ref()
.map(Path::new)
.unwrap_or_else(|| &directory_config);
.map(PathBuf::from)
.unwrap_or_else(|| directory_config.clone());
let mut io_loop = Runtime::new().expect("error when creating tokio Runtime");
let executor = io_loop.executor();
let mut catalog: Catalog = Catalog::new();
// configure our server based on the config_path
for zone in config.get_zones() {
@ -352,15 +373,12 @@ pub fn main() {
.get_zone()
.unwrap_or_else(|_| panic!("bad zone name in {:?}", config_path));
match load_zone(zone_dir, zone) {
match load_zone(&zone_dir, zone, &executor) {
Ok(authority) => catalog.upsert(zone_name.into(), authority),
Err(error) => panic!("could not load zone {}: {}", zone_name, error),
}
}
// FIXME: need a configuration for Forwarders
catalog.upsert(Name::root().into(), Box::new(ForwardAuthority::new()));
// TODO: support all the IPs asked to listen on...
// TODO:, there should be the option to listen on any port, IP and protocol option...
let v4addr = config.get_listen_addrs_ipv4();
@ -389,8 +407,6 @@ pub fn main() {
.map(|x| TcpListener::bind(x).unwrap_or_else(|_| panic!("could not bind to tcp: {}", x)))
.collect();
let mut io_loop = Runtime::new().expect("error when creating tokio Runtime");
// now, run the server, based on the config
#[cfg_attr(not(feature = "dns-over-tls"), allow(unused_mut))]
let mut server = ServerFuture::new(catalog);
@ -424,7 +440,7 @@ pub fn main() {
&mut server,
&config,
_tls_cert_config,
zone_dir,
&zone_dir,
&listen_addrs,
);
@ -435,7 +451,7 @@ pub fn main() {
&mut server,
&config,
_tls_cert_config,
zone_dir,
&zone_dir,
&listen_addrs,
);
}

View File

@ -25,29 +25,32 @@ pub fn h2_handler<T, I>(
io: I,
src_addr: SocketAddr,
dns_hostname: Arc<String>,
) -> impl Future<Item = (), Error = io::Error>
) -> impl Future<Item = (), Error = ()>
where
T: RequestHandler,
I: AsyncRead + AsyncWrite,
{
// Start the HTTP/2.0 connection handshake
server::handshake(io)
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))
.map_err(|e| warn!("h2 handshake error: {}", e))
.and_then(move |h2| {
let dns_hostname = dns_hostname.clone();
// Accept all inbound HTTP/2.0 streams sent over the
// connection.
h2.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))
h2.map_err(|e| warn!("h2 failed to receive message: {}", e))
.for_each(move |(request, respond)| {
debug!("Received request: {:#?}", request);
let dns_hostname = dns_hostname.clone();
let handler = handler.clone();
let responder = HttpsResponseHandle(respond);
let responder = HttpsResponseHandle(Arc::new(Mutex::new(respond)));
https_server::message_from(dns_hostname, request)
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))
.and_then(move |bytes| {
let message = BinDecodable::from_bytes(&bytes)?;
.map_err(|e| warn!("h2 failed to receive message: {}", e))
.and_then(|bytes| {
BinDecodable::from_bytes(&bytes)
.map_err(|e| warn!("could not decode message: {}", e))
})
.and_then(move |message| {
debug!("reieved message: {:?}", message);
server_future::handle_request(
@ -59,13 +62,14 @@ where
})
})
})
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("error in h2 handler: {}", e)))
.map_err(|_| warn!("error in h2 handler"))
}
struct HttpsResponseHandle(::h2::server::SendResponse<::bytes::Bytes>);
#[derive(Clone)]
struct HttpsResponseHandle(Arc<Mutex<::h2::server::SendResponse<::bytes::Bytes>>>);
impl ResponseHandler for HttpsResponseHandle {
fn send_response(mut self, response: MessageResponse) -> io::Result<()> {
fn send_response(&self, response: MessageResponse) -> io::Result<()> {
use bytes::Bytes;
use proto::serialize::binary::BinEncoder;
@ -84,6 +88,8 @@ impl ResponseHandler for HttpsResponseHandle {
debug!("sending response: {:#?}", response);
let mut stream = self
.0
.lock()
.expect("https poisoned")
.send_response(response, false)
.map_err(HttpsError::from)?;
stream.send_data(bytes, true).map_err(HttpsError::from)?;

View File

@ -7,24 +7,25 @@
//! Request Handler for incoming requests
use std::io;
use std::net::SocketAddr;
use futures::Future;
use authority::MessageRequest;
use server::ResponseHandler;
/// An incoming request to the DNS catalog
pub struct Request<'r> {
pub struct Request {
/// Message with the associated query or update data
pub message: MessageRequest<'r>,
pub message: MessageRequest,
/// Source address of the Client
pub src: SocketAddr,
}
/// Trait for handling incoming requests, and providing a message response.
pub trait RequestHandler: Send + 'static {
// TODO: allow associated error type
// type Error;
/// A future for execution of the request
type ResponseFuture: Future<Item = (), Error = ()> + Send + 'static;
/// Determine's what needs to happen given the type of request, i.e. Query or Update.
///
@ -32,9 +33,9 @@ pub trait RequestHandler: Send + 'static {
///
/// * `request` - the requested action to perform.
/// * `response_handle` - handle to which a return message should be sent
fn handle_request<'q, 'a, R: ResponseHandler + 'static>(
&'a self,
request: &'q Request,
fn handle_request<R: ResponseHandler>(
&self,
request: Request,
response_handle: R,
) -> io::Result<()>;
) -> Self::ResponseFuture;
}

View File

@ -15,18 +15,19 @@ use trust_dns::BufStreamHandle;
use authority::MessageResponse;
/// A handler for send a response to a client
pub trait ResponseHandler: Send {
pub trait ResponseHandler: Clone + Send + 'static {
// TODO: add associated error type
//type Error;
/// Serializes and sends a message to to the wrapped handle
///
/// self is consumed as only one message should ever be sent in response to a Request
fn send_response(self, response: MessageResponse) -> io::Result<()>;
fn send_response(&self, response: MessageResponse) -> io::Result<()>;
}
/// A handler for wraping a BufStreamHandle, which will properly serialize the message and add the
/// associated destination.
#[derive(Clone)]
pub struct ResponseHandle {
dst: SocketAddr,
stream_handle: BufStreamHandle,
@ -43,7 +44,7 @@ impl ResponseHandler for ResponseHandle {
/// Serializes and sends a message to to the wrapped handle
///
/// self is consumed as only one message should ever be sent in response to a Request
fn send_response(self, response: MessageResponse) -> io::Result<()> {
fn send_response(&self, response: MessageResponse) -> io::Result<()> {
info!(
"response: {} response_code: {}",
response.header().id(),

View File

@ -10,7 +10,7 @@ use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use futures::{Future, Stream};
use futures::{Async, Future, Poll, Stream};
#[cfg(feature = "dns-over-rustls")]
use rustls::{Certificate, PrivateKey};
@ -57,18 +57,12 @@ impl<T: RequestHandler> ServerFuture<T> {
// this spawns a ForEach future which handles all the requests into a Handler.
tokio_executor::spawn(
buf_stream
.map_err(|e| panic!("error in UDP request_stream handler: {}", e))
.for_each(move |message| {
let src_addr = message.addr();
debug!("received udp request from: {}", src_addr);
self::handle_raw_request(message, handler.clone(), stream_handle.clone())
.map_err(move |e| {
debug!("error parsing UDP request src: {:?} error: {}", src_addr, e)
})
.ok();
// continue processing...
Ok(())
})
.map_err(|e| panic!("error in UDP request_stream handler: {}", e)),
}),
);
}
@ -115,18 +109,18 @@ impl<T: RequestHandler> ServerFuture<T> {
// and spawn to the io_loop
tokio_executor::spawn(
timeout_stream
.map_err(move |e| {
debug!(
"error in TCP request_stream src: {:?} error: {}",
src_addr, e
)
})
.for_each(move |message| {
self::handle_raw_request(
message,
handler.clone(),
stream_handle.clone(),
)
})
.map_err(move |e| {
debug!(
"error in TCP request_stream src: {:?} error: {}",
src_addr, e
)
}),
);
@ -218,6 +212,12 @@ impl<T: RequestHandler> ServerFuture<T> {
// and spawn to the io_loop
tokio_executor::spawn(
timeout_stream
.map_err(move |e| {
debug!(
"error in TLS request_stream src: {:?} error: {}",
src_addr, e
)
})
.for_each(move |message| {
self::handle_raw_request(
message,
@ -225,11 +225,8 @@ impl<T: RequestHandler> ServerFuture<T> {
stream_handle.clone(),
)
})
.map_err(move |e| {
debug!(
"error in TLS request_stream src: {:?} error: {}",
src_addr, e
)
.map_err(move |_| {
debug!("error in TLS request_stream src: {:?}", src_addr)
}),
);
@ -267,7 +264,7 @@ impl<T: RequestHandler> ServerFuture<T> {
certificate_and_key: ((X509, Option<Stack<X509>>), PKey<Private>),
) -> io::Result<()> {
self.register_tls_listener(
tokio_tcp::TcpListener::from_std(listener, &Handle::current())?,
tokio_tcp::TcpListener::from_std(listener, &Handle::default())?,
timeout,
certificate_and_key,
)
@ -338,6 +335,12 @@ impl<T: RequestHandler> ServerFuture<T> {
// and spawn to the io_loop
tokio_executor::spawn(
timeout_stream
.map_err(move |e| {
debug!(
"error in TLS request_stream src: {:?} error: {}",
src_addr, e
)
})
.for_each(move |message| {
self::handle_raw_request(
message,
@ -345,11 +348,8 @@ impl<T: RequestHandler> ServerFuture<T> {
stream_handle.clone(),
)
})
.map_err(move |e| {
debug!(
"error in TLS request_stream src: {:?} error: {}",
src_addr, e
)
.map_err(move |_| {
debug!("error in TLS request_stream src: {:?}", src_addr)
}),
);
@ -440,6 +440,7 @@ impl<T: RequestHandler> ServerFuture<T> {
listener
.incoming()
.map_err(|e| warn!("error in inbound https_stream: {}", e))
.for_each(move |tcp_stream| {
let src_addr = tcp_stream.peer_addr().unwrap();
debug!("accepted request from: {}", src_addr);
@ -450,12 +451,7 @@ impl<T: RequestHandler> ServerFuture<T> {
// take the created stream...
tls_acceptor
.accept(tcp_stream)
.map_err(|e| {
io::Error::new(
io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e),
)
})
.map_err(|e| warn!("tls error: {}", e))
.and_then(move |tls_stream| {
h2_handler(handler, tls_stream, src_addr, dns_hostname)
})
@ -464,7 +460,7 @@ impl<T: RequestHandler> ServerFuture<T> {
// debug!("error HTTPS handshake: {:?} error: {:?}", src_addr, e)
// })
})
.map_err(|e| panic!("error in inbound https_stream: {}", e))
.map_err(|_| panic!("error in inbound https_stream"))
}));
Ok(())
@ -475,7 +471,7 @@ pub(crate) fn handle_raw_request<T: RequestHandler>(
message: SerialMessage,
request_handler: Arc<Mutex<T>>,
response_handler: BufStreamHandle,
) -> io::Result<()> {
) -> HandleRawRequest<T::ResponseFuture> {
let src_addr = message.addr();
let response_handler = ResponseHandle::new(message.addr(), response_handler);
@ -484,16 +480,22 @@ pub(crate) fn handle_raw_request<T: RequestHandler>(
// the IO thread.
// decode any messages that are ready
let mut decoder = BinDecoder::new(message.bytes());
let message = MessageRequest::read(&mut decoder)?;
self::handle_request(message, src_addr, request_handler, response_handler)
match MessageRequest::read(&mut decoder) {
Ok(message) => {
let handle_request =
self::handle_request(message, src_addr, request_handler, response_handler);
HandleRawRequest::HandleRequest(handle_request)
}
Err(e) => HandleRawRequest::Result(e.into()),
}
}
pub(crate) fn handle_request<'q, R: ResponseHandler + 'static, T: RequestHandler>(
message: MessageRequest<'q>,
pub(crate) fn handle_request<R: ResponseHandler, T: RequestHandler>(
message: MessageRequest,
src_addr: SocketAddr,
request_handler: Arc<Mutex<T>>,
response_handler: R,
) -> io::Result<()> {
) -> T::ResponseFuture {
let request = Request {
message,
src: src_addr,
@ -519,5 +521,26 @@ pub(crate) fn handle_request<'q, R: ResponseHandler + 'static, T: RequestHandler
request_handler
.lock()
.expect("poisoned lock")
.handle_request(&request, response_handler)
.handle_request(request, response_handler)
}
#[must_use = "futures do nothing unless polled"]
pub(crate) enum HandleRawRequest<F: Future<Item = (), Error = ()>> {
HandleRequest(F),
Result(io::Error),
}
impl<F: Future<Item = (), Error = ()>> Future for HandleRawRequest<F> {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self {
HandleRawRequest::HandleRequest(f) => f.poll(),
HandleRawRequest::Result(res) => {
warn!("failed to handle message: {}", res);
Ok(Async::Ready(()))
}
}
}
}

View File

@ -8,6 +8,8 @@
//! Configuration for the stores
use store::file::FileConfig;
#[cfg(feature = "trust-dns-resolver")]
use store::forwarder::ForwardConfig;
use store::sqlite::SqliteConfig;
/// Enumeration over all Store configurations
@ -19,4 +21,7 @@ pub enum StoreConfig {
File(FileConfig),
/// Sqlite based configuration file
Sqlite(SqliteConfig),
/// Forwarder, aka Resolver
#[cfg(feature = "trust-dns-resolver")]
Forward(ForwardConfig),
}

View File

@ -13,12 +13,15 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use futures::future::{self, Future, FutureResult, IntoFuture};
use trust_dns::op::{LowerQuery, ResponseCode};
use trust_dns::rr::dnssec::{DnsSecResult, Signer, SupportedAlgorithms};
use trust_dns::rr::{DNSClass, LowerName, Name, RData, Record, RecordSet, RecordType, RrKey};
use authority::{
AnyRecords, AuthLookup, Authority, LookupRecords, LookupError, LookupResult, MessageRequest, UpdateResult, ZoneType,
AnyRecords, AuthLookup, Authority, LookupError, LookupRecords, LookupResult, MessageRequest,
UpdateResult, ZoneType,
};
use store::file::FileConfig;
@ -134,13 +137,13 @@ impl FileAuthority {
/// Returns the minimum ttl (as used in the SOA record)
pub fn minimum_ttl(&self) -> u32 {
let soa = Authority::soa(self);
let soa = Authority::soa(self).wait(/*TODO: this works because the future here is always complete*/);
let soa = match soa {
Ok(soa) => soa,
Err(e) => {
warn!("could not lookup SOA for authority: {}: {}", self.origin, e);
return 0
return 0;
}
};
@ -155,13 +158,13 @@ impl FileAuthority {
/// get the current serial number for the zone.
pub fn serial(&self) -> u32 {
let soa = Authority::soa(self);
let soa = Authority::soa(self).wait(/*TODO: this works because the future here is always complete*/);
let soa = match soa {
Ok(soa) => soa,
Err(e) => {
warn!("could not lookup SOA for authority: {}: {}", self.origin, e);
return 0
return 0;
}
};
@ -182,13 +185,13 @@ impl FileAuthority {
#[allow(unused)]
fn increment_soa_serial(&mut self) -> u32 {
let soa = Authority::soa(self);
let soa = Authority::soa(self).wait(/*TODO: this works because the future here is always complete*/);
let soa = match soa {
Ok(soa) => soa,
Err(e) => {
warn!("could not lookup SOA for authority: {}: {}", self.origin, e);
return 0
return 0;
}
};
@ -457,6 +460,7 @@ impl FileAuthority {
impl Authority for FileAuthority {
type Lookup = AuthLookup;
type LookupFuture = FutureResult<Self::Lookup, LookupError>;
/// What type is this zone
fn zone_type(&self) -> ZoneType {
@ -554,7 +558,7 @@ impl Authority for FileAuthority {
rtype: RecordType,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<AuthLookup> {
) -> Self::LookupFuture {
let rr_key = RrKey::new(name.clone(), rtype);
// Collect the records from each rr_set
@ -569,12 +573,16 @@ impl Authority for FileAuthority {
);
Ok(LookupRecords::AnyRecords(result))
}
_ => self
.records
.get(&rr_key)
.map_or(Err(LookupError::from(ResponseCode::NXDomain)), |rr_set| {
Ok(LookupRecords::new(is_secure, supported_algorithms, rr_set.clone()))
}),
_ => self.records.get(&rr_key).map_or(
Err(LookupError::from(ResponseCode::NXDomain)),
|rr_set| {
Ok(LookupRecords::new(
is_secure,
supported_algorithms,
rr_set.clone(),
))
},
),
};
// This is annoying. The 1035 spec literally specifies that most DNS authorities would want to store
@ -583,17 +591,21 @@ impl Authority for FileAuthority {
// generally return NoError and no results when other types exist at the same name. bah.
let result = match result {
Err(LookupError::ResponseCode(ResponseCode::NXDomain)) => {
if self.records.keys().any(|key| key.name() == name) {
return Err(LookupError::NameExists);
if self
.records
.keys()
.any(|key| key.name() == name || name.zone_of(key.name()))
{
return Err(LookupError::NameExists).into_future();
} else {
return Err(LookupError::from(ResponseCode::NXDomain));
return Err(LookupError::from(ResponseCode::NXDomain)).into_future();
}
},
Err(e) => return Err(e),
}
Err(e) => return Err(e).into_future(),
o => o,
};
result.map(AuthLookup::from)
result.map(AuthLookup::from).into_future()
}
// FIXME: move back to a suplied method in the trait
@ -602,8 +614,8 @@ impl Authority for FileAuthority {
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
debug!("searching SqliteAuthority for: {}", query);
) -> Box<Future<Item = Self::Lookup, Error = LookupError> + Send> {
debug!("searching FileAuthority for: {}", query);
let lookup_name = query.name();
let record_type: RecordType = query.query_type();
@ -612,58 +624,42 @@ impl Authority for FileAuthority {
// for AXFR the first and last record must be the SOA
if RecordType::AXFR == record_type {
// TODO: support more advanced AXFR options
if !Authority::is_axfr_allowed(self) {
return Err(LookupError::from(ResponseCode::Refused));
if !self.is_axfr_allowed() {
return Box::new(Err(LookupError::from(ResponseCode::Refused)).into_future());
}
match Authority::zone_type(self) {
match self.zone_type() {
ZoneType::Master | ZoneType::Slave => (),
// TODO: Forward?
_ => return Err(LookupError::from(ResponseCode::NXDomain)), // TODO: this sould be an error.
_ => return Box::new(Err(LookupError::from(ResponseCode::NXDomain)).into_future()),
}
}
// perform the actual lookup
match record_type {
RecordType::SOA => Authority::lookup(
self,
Authority::origin(self),
record_type,
is_secure,
supported_algorithms,
),
RecordType::SOA => {
Box::new(self.lookup(self.origin(), record_type, is_secure, supported_algorithms))
}
RecordType::AXFR => {
// TODO: shouldn't these SOA's be secure? at least the first, perhaps not the last?
let start_soa = self.soa_secure(is_secure, supported_algorithms);
let end_soa = self.soa();
let records =
self.lookup(lookup_name, record_type, is_secure, supported_algorithms);
let lookup = self
.soa_secure(is_secure, supported_algorithms)
.join3(
self.soa(),
self.lookup(lookup_name, record_type, is_secure, supported_algorithms),
)
.map(|(start_soa, end_soa, records)| match start_soa {
l @ AuthLookup::Empty => l,
start_soa => AuthLookup::AXFR {
start_soa: start_soa.unwrap_records(),
records: records.unwrap_records(),
end_soa: end_soa.unwrap_records(),
},
});
let (start_soa, end_soa, records) = match (start_soa, end_soa, records) {
(Err(e), _, _) => return Err(e),
(_, Err(e), _) => return Err(e),
(_, _, Err(e)) => return Err(e),
(Ok(ss), Ok(es), Ok(r)) => (ss, es, r),
};
let lookup = match start_soa {
l @ AuthLookup::Empty => l,
start_soa => AuthLookup::AXFR {
start_soa: start_soa.unwrap_records(),
records: records.unwrap_records(),
end_soa: end_soa.unwrap_records(),
},
};
Ok(lookup)
Box::new(lookup)
}
_ => Authority::lookup(
self,
lookup_name,
record_type,
is_secure,
supported_algorithms,
),
_ => Box::new(self.lookup(lookup_name, record_type, is_secure, supported_algorithms)),
}
}
@ -680,26 +676,24 @@ impl Authority for FileAuthority {
name: &LowerName,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<AuthLookup> {
#[cfg(feature = "dnssec")]
fn is_nsec_rrset(rr_set: &RecordSet) -> bool {
use trust_dns::rr::rdata::DNSSECRecordType;
) -> Self::LookupFuture {
use trust_dns::rr::rdata::DNSSECRecordType;
fn is_nsec_rrset(rr_set: &RecordSet) -> bool {
rr_set.record_type() == RecordType::DNSSEC(DNSSECRecordType::NSEC)
}
// TODO: need a BorrowdRrKey
let rr_key = RrKey::new(name.clone(), RecordType::DNSSEC(DNSSECRecordType::NSEC));
let no_data = self.records
let no_data = self
.records
.get(&rr_key)
.map(|rr_set| {
LookupRecords::new(is_secure, supported_algorithms, rr_set.clone())
});
.map(|rr_set| LookupRecords::new(is_secure, supported_algorithms, rr_set.clone()));
if no_data.is_some() {
return no_data.unwrap().into()
return future::result(Ok(no_data.unwrap().into()));
}
let get_closest_nsec = |name: &LowerName| -> Option<Arc<RecordSet>> {
self.records
.values()
@ -709,7 +703,8 @@ impl Authority for FileAuthority {
// now find the next record where the covered name is greater
.find(|rr_set| {
// there should only be one record
rr_set.records(false, SupportedAlgorithms::default())
rr_set
.records(false, SupportedAlgorithms::default())
.next()
.and_then(|r| r.rdata().as_dnssec())
.and_then(|r| r.as_nsec())
@ -749,13 +744,16 @@ impl Authority for FileAuthority {
vec![closest_proof]
}
}
(None, Some(proof)) | (Some(proof), None) => {
vec![proof]
}
(None, None) => { vec![] }
(None, Some(proof)) | (Some(proof), None) => vec![proof],
(None, None) => vec![],
};
LookupRecords::many(is_secure, supported_algorithms, proofs).into()
future::result(Ok(LookupRecords::many(
is_secure,
supported_algorithms,
proofs,
)
.into()))
}
#[cfg(not(feature = "dnssec"))]
@ -764,8 +762,8 @@ impl Authority for FileAuthority {
_name: &LowerName,
_is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> AuthLookup {
AuthLookup::default()
) -> Self::LookupFuture {
future::result(Ok(AuthLookup::default()))
}
/// By adding a secure key, this will implicitly enable dnssec for the zone.
@ -849,7 +847,9 @@ mod tests {
RecordType::A,
false,
SupportedAlgorithms::new(),
).expect("lookup failed");
)
.wait()
.expect("lookup failed");
match lookup
.into_iter()

View File

@ -5,35 +5,70 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::borrow::Borrow;
use futures::{Async, Future, Poll};
use trust_dns::op::LowerQuery;
use trust_dns::op::ResponseCode;
use trust_dns::rr::dnssec::{DnsSecResult, Signer, SupportedAlgorithms};
use trust_dns::rr::dnssec::SupportedAlgorithms;
use trust_dns::rr::{LowerName, Name, Record, RecordType};
use trust_dns_resolver::config::ResolverConfig;
use trust_dns_resolver::lookup::Lookup as ResolverLookup;
use trust_dns_resolver::Resolver;
use trust_dns_resolver::{AsyncResolver, BackgroundLookup};
use authority::{Authority, LookupObject, LookupResult, MessageRequest, UpdateResult, ZoneType};
use authority::{Authority, LookupError, LookupObject, MessageRequest, UpdateResult, ZoneType};
use store::forwarder::ForwardConfig;
/// An authority that will forward resolutions to upstream resolvers.
///
/// This uses the trust-dns-resolver for resolving requests.
pub struct ForwardAuthority {
origin: LowerName,
/// FIXME: need to change Authority to be Async
resolver: Resolver,
resolver: AsyncResolver,
}
impl ForwardAuthority {
// FIXME: read from configuration
/// FIXME: drop this?
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
// FIXME: error here
let (resolver, bg) = AsyncResolver::from_system_conf().unwrap();
let _bg = Box::new(bg);
ForwardAuthority {
origin: Name::root().into(),
resolver: Resolver::from_system_conf().unwrap(),
resolver,
}
}
/// Read the Authority for the origin from the specified configuration
pub fn try_from_config(
origin: Name,
_zone_type: ZoneType,
config: &ForwardConfig,
) -> Result<(Self, impl Future<Item = (), Error = ()>), String> {
info!("loading forwarder config: {}", origin);
let name_servers = config.name_servers.clone();
let options = config.options.unwrap_or_default();
let config = ResolverConfig::from_parts(None, vec![], name_servers);
let (resolver, bg) = AsyncResolver::new(config, options);
info!("forward resolver configured: {}: ", origin);
Ok((
ForwardAuthority {
origin: origin.into(),
resolver,
},
bg,
))
}
}
impl Authority for ForwardAuthority {
type Lookup = ForwardLookup;
type LookupFuture = ForwardLookupFuture;
/// Always Forward
fn zone_type(&self) -> ZoneType {
@ -65,15 +100,12 @@ impl Authority for ForwardAuthority {
rtype: RecordType,
_is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
) -> Self::LookupFuture {
// FIXME: make this an error
assert!(self.origin.zone_of(name));
Ok(ForwardLookup(
self.resolver
.lookup(&Borrow::<Name>::borrow(name).to_utf8(), rtype)
.unwrap(),
))
info!("forwarding lookup: {} {}", name, rtype);
ForwardLookupFuture(self.resolver.lookup(name, rtype))
}
fn search(
@ -81,13 +113,13 @@ impl Authority for ForwardAuthority {
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
self.lookup(
) -> Box<Future<Item = Self::Lookup, Error = LookupError> + Send> {
Box::new(self.lookup(
query.name(),
query.query_type(),
is_secure,
supported_algorithms,
)
))
}
fn get_nsec_records(
@ -95,7 +127,7 @@ impl Authority for ForwardAuthority {
_name: &LowerName,
_is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
) -> Self::LookupFuture {
unimplemented!()
}
}
@ -111,3 +143,18 @@ impl LookupObject for ForwardLookup {
Box::new(self.0.record_iter())
}
}
pub struct ForwardLookupFuture(BackgroundLookup);
impl Future for ForwardLookupFuture {
type Item = ForwardLookup;
type Error = LookupError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.0.poll() {
Ok(Async::Ready(f)) => Ok(Async::Ready(ForwardLookup(f))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => Err(e.into()),
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use trust_dns_resolver::config::{NameServerConfigGroup, ResolverOpts};
/// Configuration for master file based zones
#[derive(Deserialize, PartialEq, Debug)]
pub struct ForwardConfig {
/// upstream name_server configurations
pub name_servers: NameServerConfigGroup,
/// Resolver options
pub options: Option<ResolverOpts>,
}

View File

@ -7,6 +7,10 @@
#![cfg(feature = "trust-dns-resolver")]
//! Forwarding, resolver, related types
mod authority;
mod config;
pub use self::authority::ForwardAuthority;
pub use self::config::ForwardConfig;

View File

@ -13,6 +13,8 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use futures::future::{self, Future, FutureResult, IntoFuture};
use trust_dns::op::LowerQuery;
use trust_dns::op::ResponseCode;
use trust_dns::proto::rr::dnssec::rdata::key::KEY;
@ -24,7 +26,8 @@ use trust_dns::rr::{DNSClass, LowerName, Name, RData, Record, RecordSet, RecordT
#[cfg(feature = "dnssec")]
use authority::UpdateRequest;
use authority::{
AnyRecords, AuthLookup, Authority, LookupRecords, LookupError, LookupResult, MessageRequest, UpdateResult, ZoneType,
AnyRecords, AuthLookup, Authority, LookupError, LookupRecords, LookupResult, MessageRequest,
UpdateResult, ZoneType,
};
use store::sqlite::{Journal, SqliteConfig};
@ -261,13 +264,11 @@ impl SqliteAuthority {
/// Returns the minimum ttl (as used in the SOA record)
pub fn minimum_ttl(&self) -> u32 {
let soa = match self.soa() {
let soa = match self.soa().wait(/*TODO: this works because the future here is always complete*/)
{
Ok(soa) => soa,
Err(e) => {
error!(
"error finding soa record for zone: {}: {}",
self.origin, e
);
error!("error finding soa record for zone: {}: {}", self.origin, e);
return 0;
}
};
@ -283,13 +284,11 @@ impl SqliteAuthority {
/// get the current serial number for the zone.
pub fn serial(&self) -> u32 {
let soa = match self.soa() {
let soa = match self.soa().wait(/*TODO: this works because the future here is always complete*/)
{
Ok(soa) => soa,
Err(e) => {
error!(
"error finding soa record for zone: {}: {}",
self.origin, e
);
error!("error finding soa record for zone: {}: {}", self.origin, e);
return 0;
}
};
@ -310,7 +309,8 @@ impl SqliteAuthority {
}
fn increment_soa_serial(&mut self) -> u32 {
let soa = match self.soa() {
let soa = match self.soa().wait(/*TODO: this works because the future here is always complete*/)
{
Ok(soa) => soa,
Err(e) => {
error!(
@ -456,6 +456,7 @@ impl SqliteAuthority {
false,
SupportedAlgorithms::new(),
)
.wait(/*TODO: this works because the future here is always complete*/)
.unwrap_or_default()
.was_empty()
{
@ -473,6 +474,7 @@ impl SqliteAuthority {
false,
SupportedAlgorithms::new(),
)
.wait(/*TODO: this works because the future here is always complete*/)
.unwrap_or_default()
.was_empty()
{
@ -498,6 +500,7 @@ impl SqliteAuthority {
false,
SupportedAlgorithms::new(),
)
.wait(/*TODO: this works because the future here is always complete*/)
.unwrap_or_default()
.was_empty()
{
@ -515,6 +518,7 @@ impl SqliteAuthority {
false,
SupportedAlgorithms::new(),
)
.wait(/*TODO: this works because the future here is always complete*/)
.unwrap_or_default()
.was_empty()
{
@ -538,6 +542,7 @@ impl SqliteAuthority {
false,
SupportedAlgorithms::new(),
)
.wait(/*TODO: this works because the future here is always complete*/)
.unwrap_or_default()
.iter()
.find(|rr| *rr == require)
@ -618,16 +623,19 @@ impl SqliteAuthority {
})
.any(|sig| {
let name = LowerName::from(sig.signer_name());
let keys = self.lookup(
&name,
RecordType::DNSSEC(DNSSECRecordType::KEY),
false,
SupportedAlgorithms::new(),
);
// TODO: updates should be async as well.
let keys = self
.lookup(
&name,
RecordType::DNSSEC(DNSSECRecordType::KEY),
false,
SupportedAlgorithms::new(),
)
.wait();
let keys = match keys {
Ok(keys) => keys,
Err(e) => return false,
Err(_) => return false,
};
debug!("found keys {:?}", keys);
@ -1164,6 +1172,7 @@ impl SqliteAuthority {
impl Authority for SqliteAuthority {
type Lookup = AuthLookup;
type LookupFuture = FutureResult<Self::Lookup, LookupError>;
/// What type is this zone
fn zone_type(&self) -> ZoneType {
@ -1273,7 +1282,7 @@ impl Authority for SqliteAuthority {
rtype: RecordType,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
) -> Self::LookupFuture {
let rr_key = RrKey::new(name.clone(), rtype);
// Collect the records from each rr_set
@ -1288,12 +1297,16 @@ impl Authority for SqliteAuthority {
);
Ok(LookupRecords::AnyRecords(result))
}
_ => self
.records
.get(&rr_key)
.map_or(Err(LookupError::from(ResponseCode::NXDomain)), |rr_set| {
Ok(LookupRecords::new(is_secure, supported_algorithms, rr_set.clone()))
}),
_ => self.records.get(&rr_key).map_or(
Err(LookupError::from(ResponseCode::NXDomain)),
|rr_set| {
Ok(LookupRecords::new(
is_secure,
supported_algorithms,
rr_set.clone(),
))
},
),
};
// This is annoying. The 1035 spec literally specifies that most DNS authorities would want to store
@ -1302,17 +1315,21 @@ impl Authority for SqliteAuthority {
// generally return NoError and no results when other types exist at the same name. bah.
let result = match result {
Err(LookupError::ResponseCode(ResponseCode::NXDomain)) => {
if self.records.keys().any(|key| key.name() == name) {
return Err(LookupError::NameExists);
if self
.records
.keys()
.any(|key| key.name() == name || name.zone_of(key.name()))
{
return Err(LookupError::NameExists).into_future();
} else {
return Err(LookupError::from(ResponseCode::NXDomain));
return Err(LookupError::from(ResponseCode::NXDomain)).into_future();
}
},
Err(e) => return Err(e),
}
Err(e) => return Err(e).into_future(),
o => o,
};
result.map(AuthLookup::from)
result.map(AuthLookup::from).into_future()
}
// FIXME: move back to a suplied method in the trait
@ -1321,7 +1338,7 @@ impl Authority for SqliteAuthority {
query: &LowerQuery,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
) -> Box<Future<Item = Self::Lookup, Error = LookupError> + Send> {
debug!("searching SqliteAuthority for: {}", query);
let lookup_name = query.name();
@ -1332,47 +1349,41 @@ impl Authority for SqliteAuthority {
if RecordType::AXFR == record_type {
// TODO: support more advanced AXFR options
if !self.is_axfr_allowed() {
return Err(LookupError::from(ResponseCode::Refused))
return Box::new(Err(LookupError::from(ResponseCode::Refused)).into_future());
}
match self.zone_type() {
ZoneType::Master | ZoneType::Slave => (),
// TODO: Forward?
_ => return Err(LookupError::from(ResponseCode::NXDomain)),
_ => return Box::new(Err(LookupError::from(ResponseCode::NXDomain)).into_future()),
}
}
// perform the actual lookup
match record_type {
RecordType::SOA => {
self.lookup(self.origin(), record_type, is_secure, supported_algorithms)
Box::new(self.lookup(self.origin(), record_type, is_secure, supported_algorithms))
}
RecordType::AXFR => {
// TODO: shouldn't these SOA's be secure? at least the first, perhaps not the last?
let start_soa = self.soa_secure(is_secure, supported_algorithms);
let end_soa = self.soa();
let records =
self.lookup(lookup_name, record_type, is_secure, supported_algorithms);
let lookup = self
.soa_secure(is_secure, supported_algorithms)
.join3(
self.soa(),
self.lookup(lookup_name, record_type, is_secure, supported_algorithms),
)
.map(|(start_soa, end_soa, records)| match start_soa {
l @ AuthLookup::Empty => l,
start_soa => AuthLookup::AXFR {
start_soa: start_soa.unwrap_records(),
records: records.unwrap_records(),
end_soa: end_soa.unwrap_records(),
},
});
let (start_soa, end_soa, records) = match (start_soa, end_soa, records) {
(Err(e), _, _) => return Err(e),
(_, Err(e), _) => return Err(e),
(_, _, Err(e)) => return Err(e),
(Ok(ss), Ok(es), Ok(r)) => (ss, es, r),
};
let lookup = match start_soa {
l @ AuthLookup::Empty => l,
start_soa => AuthLookup::AXFR {
start_soa: start_soa.unwrap_records(),
records: records.unwrap_records(),
end_soa: end_soa.unwrap_records(),
},
};
Ok(lookup)
Box::new(lookup)
}
_ => self.lookup(lookup_name, record_type, is_secure, supported_algorithms),
_ => Box::new(self.lookup(lookup_name, record_type, is_secure, supported_algorithms)),
}
}
@ -1389,26 +1400,24 @@ impl Authority for SqliteAuthority {
name: &LowerName,
is_secure: bool,
supported_algorithms: SupportedAlgorithms,
) -> LookupResult<Self::Lookup> {
#[cfg(feature = "dnssec")]
fn is_nsec_rrset(rr_set: &RecordSet) -> bool {
use trust_dns::rr::rdata::DNSSECRecordType;
) -> Self::LookupFuture {
use trust_dns::rr::rdata::DNSSECRecordType;
fn is_nsec_rrset(rr_set: &RecordSet) -> bool {
rr_set.record_type() == RecordType::DNSSEC(DNSSECRecordType::NSEC)
}
// TODO: need a BorrowdRrKey
let rr_key = RrKey::new(name.clone(), RecordType::DNSSEC(DNSSECRecordType::NSEC));
let no_data = self.records
let no_data = self
.records
.get(&rr_key)
.map(|rr_set| {
LookupRecords::new(is_secure, supported_algorithms, rr_set.clone())
});
.map(|rr_set| LookupRecords::new(is_secure, supported_algorithms, rr_set.clone()));
if no_data.is_some() {
return no_data.unwrap().into()
return future::result(Ok(no_data.unwrap().into()));
}
let get_closest_nsec = |name: &LowerName| -> Option<Arc<RecordSet>> {
self.records
.values()
@ -1418,7 +1427,8 @@ impl Authority for SqliteAuthority {
// now find the next record where the covered name is greater
.find(|rr_set| {
// there should only be one record
rr_set.records(false, SupportedAlgorithms::default())
rr_set
.records(false, SupportedAlgorithms::default())
.next()
.and_then(|r| r.rdata().as_dnssec())
.and_then(|r| r.as_nsec())
@ -1458,13 +1468,16 @@ impl Authority for SqliteAuthority {
vec![closest_proof]
}
}
(None, Some(proof)) | (Some(proof), None) => {
vec![proof]
}
(None, None) => { vec![] }
(None, Some(proof)) | (Some(proof), None) => vec![proof],
(None, None) => vec![],
};
LookupRecords::many(is_secure, supported_algorithms, proofs).into()
future::result(Ok(LookupRecords::many(
is_secure,
supported_algorithms,
proofs,
)
.into()))
}
#[cfg(not(feature = "dnssec"))]
@ -1473,8 +1486,8 @@ impl Authority for SqliteAuthority {
_name: &LowerName,
_is_secure: bool,
_supported_algorithms: SupportedAlgorithms,
) -> AuthLookup {
AuthLookup::default()
) -> Self::LookupFuture {
future::result(Ok(AuthLookup::default()))
}
#[cfg(feature = "dnssec")]

View File

@ -9,6 +9,7 @@
use std::iter::Iterator;
use std::path::Path;
use std::sync::{Mutex, MutexGuard};
use rusqlite::{self, types::ToSql, Connection};
use time;
@ -23,7 +24,7 @@ pub const CURRENT_VERSION: i64 = 1;
/// The Journal is the audit log of all changes to a zone after initial creation.
pub struct Journal {
conn: Connection,
conn: Mutex<Connection>,
version: i64,
}
@ -31,7 +32,10 @@ impl Journal {
/// Constructs a new Journal, attaching to the specified Sqlite Connection
pub fn new(conn: Connection) -> PersistenceResult<Journal> {
let version = Self::select_schema_version(&conn)?;
Ok(Journal { conn, version })
Ok(Journal {
conn: Mutex::new(conn),
version,
})
}
/// Constructs a new Journal opening a Sqlite connection to the file at the specified path
@ -47,8 +51,8 @@ impl Journal {
}
/// Returns a reference to the Sqlite Connection
pub fn conn(&self) -> &Connection {
&self.conn
pub fn conn(&self) -> MutexGuard<Connection> {
self.conn.lock().expect("conn poisoned")
}
/// Returns the current schema version of the journal
@ -86,7 +90,7 @@ impl Journal {
let client_id: i64 = 0; // TODO: we need better id information about the client, like pub_key
let soa_serial: i64 = i64::from(soa_serial);
let count = self.conn.execute(
let count = self.conn.lock().expect("conn poisoned").execute(
"INSERT
\
INTO records (client_id, soa_serial, timestamp, \
@ -137,7 +141,8 @@ impl Journal {
"schema version mismatch, schema_up() resolves this"
);
let mut stmt = self.conn.prepare(
let conn = self.conn.lock().expect("conn poisoned");
let mut stmt = conn.prepare(
"SELECT _rowid_, record
\
FROM records
@ -222,6 +227,8 @@ impl Journal {
let count = self
.conn
.lock()
.expect("conn poisoned")
.execute("UPDATE tdns_schema SET version = $1", &[&new_version])?;
//
@ -246,7 +253,7 @@ impl Journal {
/// initial schema, include the tdns_schema table for tracking the Journal version
fn init_up(&self) -> PersistenceResult<i64> {
let count = self.conn.execute(
let count = self.conn.lock().expect("conn poisoned").execute(
"CREATE TABLE tdns_schema (
\
version INTEGER NOT NULL
@ -257,7 +264,7 @@ impl Journal {
//
assert_eq!(count, 0);
let count = self.conn.execute(
let count = self.conn.lock().expect("conn poisoned").execute(
"INSERT INTO tdns_schema (version) VALUES (0)",
None::<&dyn ToSql>,
)?;
@ -271,7 +278,7 @@ impl Journal {
/// authority. Each record is expected to be in the format of an update record
fn records_up(&self) -> PersistenceResult<i64> {
// we'll be using rowid for our primary key, basically: `rowid INTEGER PRIMARY KEY ASC`
let count = self.conn.execute(
let count = self.conn.lock().expect("conn poisoned").execute(
"CREATE TABLE records (
\
client_id INTEGER NOT NULL,

View File

@ -1,6 +1,8 @@
use std::net::Ipv4Addr;
use std::str::FromStr;
use futures::future::Future;
use trust_dns::op::{Message, Query};
use trust_dns::rr::dnssec::SupportedAlgorithms;
use trust_dns::rr::{Name, RData, RecordType};
@ -9,7 +11,7 @@ use trust_dns_server::authority::{AuthLookup, Authority, MessageRequest};
pub fn test_a_lookup<A: Authority<Lookup = AuthLookup>>(authority: A) {
let query = Query::query(Name::from_str("www.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).wait().unwrap();
match lookup
.into_iter()
@ -24,7 +26,7 @@ pub fn test_a_lookup<A: Authority<Lookup = AuthLookup>>(authority: A) {
#[allow(clippy::unreadable_literal)]
pub fn test_soa<A: Authority<Lookup = AuthLookup>>(authority: A) {
let lookup = authority.soa().unwrap();
let lookup = authority.soa().wait().unwrap();
match lookup
.into_iter()
@ -46,7 +48,7 @@ pub fn test_soa<A: Authority<Lookup = AuthLookup>>(authority: A) {
}
pub fn test_ns<A: Authority<Lookup = AuthLookup>>(authority: A) {
let lookup = authority.ns(false, SupportedAlgorithms::new()).unwrap();
let lookup = authority.ns(false, SupportedAlgorithms::new()).wait().unwrap();
match lookup
.into_iter()
@ -70,9 +72,9 @@ pub fn test_update_errors<A: Authority<Lookup = AuthLookup>>(mut authority: A) {
assert!(authority.update(&update).is_err());
}
pub fn test_dots_in_name<A: Authority>(authority: A) {
pub fn test_dots_in_name<A: Authority<Lookup = AuthLookup>>(authority: A) {
let query = Query::query(Name::from_str("this.has.dots.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).wait().unwrap();
assert_eq!(
*lookup
@ -87,30 +89,26 @@ pub fn test_dots_in_name<A: Authority>(authority: A) {
// the rest should all be NameExists
let query = Query::query(Name::from_str("has.dots.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).wait().unwrap_err();
assert!(lookup.was_empty());
assert!(lookup.is_name_exists());
assert!(lookup.is_name_exists(), "lookup: {}", lookup);
// the rest should all be NameExists
let query = Query::query(Name::from_str("dots.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).wait().unwrap_err();
assert!(lookup.was_empty());
assert!(lookup.is_name_exists());
// the rest should all be NameExists
let query = Query::query(Name::from_str("example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).wait().unwrap_err();
assert!(lookup.was_empty());
assert!(lookup.is_name_exists());
// and this should be an NXDOMAIN
let query = Query::query(Name::from_str("not.this.has.dots.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).wait().unwrap_err();
assert!(lookup.was_empty());
assert!(lookup.is_nx_domain());
}

View File

@ -2,17 +2,22 @@
use std::str::FromStr;
use futures::Future;
use trust_dns::op::Query;
use trust_dns::proto::rr::dnssec::rdata::{DNSSECRecordType, DNSKEY};
use trust_dns::proto::xfer;
use trust_dns::rr::dnssec::{Algorithm, SupportedAlgorithms, Verifier};
use trust_dns::rr::{DNSClass, Name, Record, RecordType};
use trust_dns_server::authority::Authority;
use trust_dns_server::authority::{Authority, AuthLookup};
pub fn test_a_lookup<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
let query = Query::query(Name::from_str("www.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(&query.into(), true, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), true, SupportedAlgorithms::new())
.wait()
.unwrap();
let (a_records, other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -29,7 +34,10 @@ pub fn test_a_lookup<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DN
#[allow(clippy::unreadable_literal)]
pub fn test_soa<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
let lookup = authority.soa_secure(true, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.soa_secure(true, SupportedAlgorithms::new())
.wait()
.unwrap();
let (soa_records, other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -57,7 +65,10 @@ pub fn test_soa<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]
}
pub fn test_ns<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
let lookup = authority.ns(true, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.ns(true, SupportedAlgorithms::new())
.wait()
.unwrap();
let (ns_records, other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -80,7 +91,7 @@ pub fn test_ns<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY])
pub fn test_nsec_nodata<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
// this should have a single nsec record that covers the type
let name = Name::from_str("www.example.com.").unwrap();
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all());
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all()).wait().unwrap();
let (nsec_records, _other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -102,7 +113,7 @@ pub fn test_nsec_nodata<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &
pub fn test_nsec_nxdomain_start<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
// tests between the SOA and first record in the zone, where bbb is the first zone record
let name = Name::from_str("aaa.example.com.").unwrap();
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all());
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all()).wait().unwrap();
let (nsec_records, _other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -126,7 +137,7 @@ pub fn test_nsec_nxdomain_start<A: Authority<Lookup = AuthLookup>>(authority: A,
pub fn test_nsec_nxdomain_middle<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
// follows the first record, nsec should cover between ccc and www, where bbb is the first zone record
let name = Name::from_str("ccc.example.com.").unwrap();
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all());
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all()).wait().unwrap();
let (nsec_records, _other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -149,7 +160,7 @@ pub fn test_nsec_nxdomain_middle<A: Authority<Lookup = AuthLookup>>(authority: A
pub fn test_nsec_nxdomain_wraps_end<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]) {
// wraps back to the begining of the zone, where www is the last zone record
let name = Name::from_str("zzz.example.com.").unwrap();
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all());
let lookup = authority.get_nsec_records(&name.clone().into(), true, SupportedAlgorithms::all()).wait().unwrap();
let (nsec_records, _other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()
@ -179,11 +190,14 @@ pub fn test_rfc_6975_supported_algorithms<A: Authority<Lookup = AuthLookup>>(
let query = Query::query(Name::from_str("www.example.com.").unwrap(), RecordType::A);
let lookup = authority.search(
&query.into(),
true,
SupportedAlgorithms::from(key.algorithm()),
).unwrap();
let lookup = authority
.search(
&query.into(),
true,
SupportedAlgorithms::from(key.algorithm()),
)
.wait()
.unwrap();
let (a_records, other_records): (Vec<_>, Vec<_>) = lookup
.into_iter()

View File

@ -3,12 +3,16 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use futures::Future;
use trust_dns::op::update_message;
use trust_dns::op::{Message, Query, ResponseCode};
use trust_dns::proto::rr::{DNSClass, Name, RData, Record, RecordSet, RecordType};
use trust_dns::rr::dnssec::{Algorithm, Signer, SupportedAlgorithms, Verifier};
use trust_dns::serialize::binary::{BinDecodable, BinEncodable};
use trust_dns_server::authority::{AuthLookup, Authority, LookupError, MessageRequest, UpdateResult};
use trust_dns_server::authority::{
AuthLookup, Authority, LookupError, MessageRequest, UpdateResult,
};
fn update_authority<A: Authority<Lookup = AuthLookup>>(
mut message: Message,
@ -36,7 +40,10 @@ pub fn test_create<A: Authority<Lookup = AuthLookup>>(mut authority: A, keys: &[
assert!(update_authority(message, key, &mut authority).expect("create failed"));
let query = Query::query(name, RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
match lookup
.into_iter()
@ -82,7 +89,10 @@ pub fn test_create_multi<A: Authority<Lookup = AuthLookup>>(mut authority: A, ke
assert!(update_authority(message, key, &mut authority).expect("create failed"));
let query = Query::query(name, RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert!(lookup.iter().any(|rr| *rr == record));
assert!(lookup.iter().any(|rr| *rr == record2));
@ -128,7 +138,10 @@ pub fn test_append<A: Authority<Lookup = AuthLookup>>(mut authority: A, keys: &[
// verify record contents
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 1);
assert!(lookup.iter().any(|rr| *rr == record));
@ -145,7 +158,10 @@ pub fn test_append<A: Authority<Lookup = AuthLookup>>(mut authority: A, keys: &[
assert!(update_authority(message, key, &mut authority).expect("append failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 2);
@ -161,7 +177,10 @@ pub fn test_append<A: Authority<Lookup = AuthLookup>>(mut authority: A, keys: &[
assert!(!update_authority(message, key, &mut authority).expect("append failed"));
let query = Query::query(name, RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 2);
@ -204,7 +223,10 @@ pub fn test_append_multi<A: Authority<Lookup = AuthLookup>>(mut authority: A, ke
assert!(update_authority(message, key, &mut authority).expect("append failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 3);
@ -219,7 +241,10 @@ pub fn test_append_multi<A: Authority<Lookup = AuthLookup>>(mut authority: A, ke
assert!(!update_authority(message, key, &mut authority).expect("append failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 3);
@ -260,7 +285,10 @@ pub fn test_compare_and_swap<A: Authority<Lookup = AuthLookup>>(mut authority: A
assert!(update_authority(message, key, &mut authority).expect("compare_and_swap failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 1);
assert!(lookup.iter().any(|rr| *rr == new));
@ -282,7 +310,10 @@ pub fn test_compare_and_swap<A: Authority<Lookup = AuthLookup>>(mut authority: A
);
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 1);
assert!(lookup.iter().any(|rr| *rr == new));
@ -333,7 +364,10 @@ pub fn test_compare_and_swap_multi<A: Authority<Lookup = AuthLookup>>(
assert!(update_authority(message, key, &mut authority).expect("compare_and_swap failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 2);
assert!(lookup.iter().any(|rr| *rr == new1));
@ -357,7 +391,10 @@ pub fn test_compare_and_swap_multi<A: Authority<Lookup = AuthLookup>>(
);
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 2);
assert!(lookup.iter().any(|rr| *rr == new1));
@ -407,7 +444,10 @@ pub fn test_delete_by_rdata<A: Authority<Lookup = AuthLookup>>(mut authority: A,
assert!(update_authority(message, key, &mut authority).expect("delete_by_rdata failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 1);
assert!(lookup.iter().any(|rr| *rr == record1));
@ -467,7 +507,10 @@ pub fn test_delete_by_rdata_multi<A: Authority<Lookup = AuthLookup>>(
assert!(update_authority(message, key, &mut authority).expect("delete_by_rdata failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new()).unwrap();
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait()
.unwrap();
assert_eq!(lookup.iter().count(), 2);
assert!(!lookup.iter().any(|rr| *rr == record1));
@ -515,8 +558,14 @@ pub fn test_delete_rrset<A: Authority<Lookup = AuthLookup>>(mut authority: A, ke
assert!(update_authority(message, key, &mut authority).expect("delete_rrset failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
assert_eq!(lookup.unwrap_err(), LookupError::ResponseCode(ResponseCode::NXDomain));
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait();
assert_eq!(
lookup.unwrap_err(),
LookupError::ResponseCode(ResponseCode::NXDomain)
);
}
}
@ -564,12 +613,22 @@ pub fn test_delete_all<A: Authority<Lookup = AuthLookup>>(mut authority: A, keys
assert!(update_authority(message, key, &mut authority).expect("delete_all failed"));
let query = Query::query(name.clone(), RecordType::A);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
assert_eq!(lookup.unwrap_err(), LookupError::ResponseCode(ResponseCode::NXDomain));
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait();
assert_eq!(
lookup.unwrap_err(),
LookupError::ResponseCode(ResponseCode::NXDomain)
);
let query = Query::query(name.clone(), RecordType::AAAA);
let lookup = authority.search(&query.into(), false, SupportedAlgorithms::new());
assert_eq!(lookup.unwrap_err(), LookupError::ResponseCode(ResponseCode::NXDomain));
let lookup = authority
.search(&query.into(), false, SupportedAlgorithms::new())
.wait();
assert_eq!(
lookup.unwrap_err(),
LookupError::ResponseCode(ResponseCode::NXDomain)
);
}
}

View File

@ -182,7 +182,8 @@ algorithm = \
\"RSASHA256\"
signer_name = \"ns.example.com.\"
".parse()
"
.parse()
.unwrap();
assert_eq!(
config.get_zones()[0].get_keys()[0].key_path(),
@ -245,7 +246,8 @@ fn test_parse_tls() {
let config: Config = "
tls_cert = { path = \"path/to/some.pkcs12\" }
tls_listen_port = 8853
".parse()
"
.parse()
.unwrap();
assert_eq!(config.get_tls_listen_port(), 8853);
@ -285,3 +287,5 @@ define_test_config!(ipv4_only);
define_test_config!(ipv6_only);
define_test_config!(openssl_dnssec);
define_test_config!(ring_dnssec);
#[cfg(feature = "trust-dns-resolver")]
define_test_config!(example_forwarder);

View File

@ -1,25 +1,33 @@
#![recursion_limit = "128"]
#![cfg(feature = "trust-dns-resolver")]
extern crate futures;
extern crate trust_dns;
extern crate trust_dns_server;
use std::net::Ipv4Addr;
use std::str::FromStr;
use futures::future::Future;
use trust_dns::rr::{Name, RecordType};
use trust_dns_server::authority::{Authority, LookupObject};
use trust_dns_server::store::forwarder::ForwardAuthority;
#[ignore]
#[test]
fn test_lookup() {
let forwarder = ForwardAuthority::new();
let lookup = forwarder.lookup(
&Name::from_str("www.example.com.").unwrap().into(),
RecordType::A,
false,
Default::default(),
).unwrap();
let lookup = forwarder
.lookup(
&Name::from_str("www.example.com.").unwrap().into(),
RecordType::A,
false,
Default::default(),
)
.wait()
.unwrap();
let address = lookup.iter().next().expect("no addresses returned!");
let address = address.rdata().as_a().expect("not an A record");

View File

@ -0,0 +1,41 @@
## Default zones, these should be present on all nameservers, except in rare
## configuration cases
[[zones]]
zone = "localhost"
zone_type = "Master"
file = "default/localhost.zone"
[[zones]]
zone = "0.0.127.in-addr.arpa"
zone_type = "Master"
file = "default/127.0.0.1.zone"
[[zones]]
zone = "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"
zone_type = "Master"
file = "default/ipv6_1.zone"
[[zones]]
zone = "255.in-addr.arpa"
zone_type = "Master"
file = "default/255.zone"
[[zones]]
zone = "0.in-addr.arpa"
zone_type = "Master"
file = "default/0.zone"
[[zones]]
## zone: this is the ORIGIN of the zone, aka the base name, '.' is implied on the end
## specifying something other than '.' here, will restrict this forwarder to only queries
## where the search name is a subzone of the name, e.g. if zone is "example.com.", then
## queries for "www.example.com" or "example.com" would be forwarded.
zone = "."
## zone_type: Master, Slave, Hint, Forward
zone_type = "Forward"
## remember the port, defaults: 53 for Udp & Tcp, 853 for Tls and 443 for Https.
## Tls and/or Https require features dns-over-tls and/or dns-over-https
stores = { type = "forward", name_servers = [{ socket_addr = "8.8.8.8:53", protocol = "Udp" },
{ socket_addr = "8.8.8.8:53", protocol = "Tcp" }] }

View File

@ -56,7 +56,7 @@ where
.stdout(Stdio::piped())
.env(
"RUST_LOG",
"ht=trace,trust_dns_https=debug,trust_dns_proto=debug",
"trust_dns=debug,trust_dns_https=debug,trust_dns_proto=debug,trust_dns_resolver=debug,trust_dns_server=debug",
).arg("-d")
.arg(&format!(
"--config={}/tests/named_test_configs/{}",
@ -101,7 +101,8 @@ where
kill_named();
panic!("timeout");
}).expect("could not start thread killer");
})
.expect("could not start thread killer");
// we should get the correct output before 1000 lines...
let mut output = String::new();
@ -152,7 +153,8 @@ where
info!("SRV: {}", output.trim_end());
}
}
}).expect("no thread available");
})
.expect("no thread available");
println!("running test...");
@ -186,7 +188,7 @@ pub fn query_a<C: ClientHandle>(io_loop: &mut Runtime, client: &mut C) {
let record = &response.answers()[0];
if let RData::A(ref address) = *record.rdata() {
assert!(address == &Ipv4Addr::new(127, 0, 0, 1))
assert_eq!(address, &Ipv4Addr::new(127, 0, 0, 1))
} else {
panic!("wrong RDATA")
}
@ -225,7 +227,8 @@ pub fn query_all_dnssec<R: Future<Item = DnsResponse, Error = ProtoError> + Send
} else {
panic!("wrong RDATA")
}
}).find(|d| d.algorithm() == algorithm);
})
.find(|d| d.algorithm() == algorithm);
assert!(dnskey.is_some(), "DNSKEY not found");
let response = query_message(
@ -245,7 +248,8 @@ pub fn query_all_dnssec<R: Future<Item = DnsResponse, Error = ProtoError> + Send
} else {
panic!("wrong RDATA")
}
}).filter(|rrsig| rrsig.algorithm() == algorithm)
})
.filter(|rrsig| rrsig.algorithm() == algorithm)
.find(|rrsig| rrsig.type_covered() == RecordType::DNSSEC(DNSSECRecordType::DNSKEY));
assert!(rrsig.is_some(), "Associated RRSIG not found");
}

View File

@ -28,7 +28,7 @@ fn test_init_journal() {
let version = journal.schema_up().unwrap();
assert_eq!(version, CURRENT_VERSION);
assert_eq!(
Journal::select_schema_version(journal.conn()).unwrap(),
Journal::select_schema_version(&*journal.conn()).unwrap(),
CURRENT_VERSION
);
}

View File

@ -1,3 +1,4 @@
extern crate futures;
extern crate trust_dns;
extern crate trust_dns_server;

View File

@ -1,3 +1,4 @@
extern crate futures;
extern crate trust_dns;
extern crate trust_dns_server;

View File

@ -1,3 +1,4 @@
extern crate futures;
extern crate trust_dns;
extern crate trust_dns_proto;
extern crate trust_dns_server;
@ -6,6 +7,8 @@ use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use std::sync::Arc;
use futures::future::Future;
use trust_dns::proto::rr::rdata::tlsa::*;
use trust_dns::rr::dnssec::*;
use trust_dns::rr::*;
@ -89,7 +92,7 @@ _443._tcp.www.example.com. IN TLSA (
// not validating everything, just one of each...
// SOA
let soa_record = authority.soa().unwrap().iter().next().cloned().unwrap();
let soa_record = authority.soa().wait().unwrap().iter().next().cloned().unwrap();
assert_eq!(RecordType::SOA, soa_record.rr_type());
assert_eq!(&Name::from_str("isi.edu").unwrap(), soa_record.name()); // i.e. the origin or domain
assert_eq!(3_600_000, soa_record.ttl());
@ -118,6 +121,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -153,6 +157,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -187,6 +192,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -210,6 +216,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()
@ -233,6 +240,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()
@ -257,6 +265,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -303,6 +312,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()
@ -322,6 +332,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()
@ -344,6 +355,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()
@ -367,6 +379,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()
@ -390,6 +403,7 @@ _443._tcp.www.example.com. IN TLSA (
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.next()

View File

@ -6,6 +6,7 @@
// copied, modified, or distributed except according to those terms.
extern crate chrono;
extern crate env_logger;
extern crate futures;
#[macro_use]
extern crate log;
@ -157,7 +158,8 @@ fn test_nodata_where_name_exists() {
Name::from_str("www.example.com.").unwrap(),
DNSClass::IN,
RecordType::SRV,
)).unwrap();
))
.unwrap();
assert_eq!(msg.response_code(), ResponseCode::NoError);
assert!(msg.answers().is_empty());
assert!(true);
@ -179,7 +181,8 @@ fn test_nxdomain_where_no_name_exists() {
Name::from_str("nxdomain.example.com.").unwrap(),
DNSClass::IN,
RecordType::SRV,
)).unwrap();
))
.unwrap();
assert_eq!(msg.response_code(), ResponseCode::NXDomain);
assert!(msg.answers().is_empty());
assert!(true);
@ -245,3 +248,47 @@ fn test_server_continues_on_bad_data_tcp() {
query_a(&mut io_loop, &mut client);
})
}
#[test]
#[cfg(feature = "trust-dns-resolver")]
fn test_forward() {
use server_harness::query_message;
env_logger::init();
named_test_harness("example_forwarder.toml", |port, _, _| {
let mut io_loop = Runtime::new().unwrap();
let addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), port);
let (stream, sender) = TcpClientStream::new(addr);
let (bg, mut client) = ClientFuture::new(Box::new(stream), sender, None);
io_loop.spawn(bg);
let response = query_message(
&mut io_loop,
&mut client,
Name::from_str("www.example.com").unwrap(),
RecordType::A,
);
assert_eq!(
*response.answers()[0].rdata().as_a().unwrap(),
Ipv4Addr::new(93, 184, 216, 34)
);
// just tests that multiple queries work
let addr: SocketAddr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), port);
let (stream, sender) = TcpClientStream::new(addr);
let (bg, mut client) = ClientFuture::new(Box::new(stream), sender, None);
io_loop.spawn(bg);
let response = query_message(
&mut io_loop,
&mut client,
Name::from_str("www.example.com").unwrap(),
RecordType::A,
);
assert_eq!(
*response.answers()[0].rdata().as_a().unwrap(),
Ipv4Addr::new(93, 184, 216, 34)
);
})
}

View File

@ -13,14 +13,15 @@ extern crate trust_dns_server;
use std::fmt;
use std::io;
use std::mem;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use futures::stream::{Fuse, Stream};
use futures::sync::mpsc::{unbounded, UnboundedReceiver};
use futures::task;
use futures::{finished, Async, Future, Poll};
use futures::{finished, future, task, Async, Future, Poll};
use tokio_timer::Delay;
use trust_dns::client::ClientConnection;
@ -69,33 +70,51 @@ impl TestClientStream {
#[derive(Clone, Default)]
pub struct TestResponseHandler {
message_ready: Arc<AtomicBool>,
buf: Arc<Mutex<Vec<u8>>>,
}
impl TestResponseHandler {
pub fn new() -> Self {
let buf = Arc::new(Mutex::new(Vec::with_capacity(512)));
TestResponseHandler { buf }
let message_ready = Arc::new(AtomicBool::new(false));
TestResponseHandler { message_ready, buf }
}
pub fn into_inner(self) -> Vec<u8> {
Arc::try_unwrap(self.buf).unwrap().into_inner().unwrap()
fn into_inner(self) -> impl Future<Item = Vec<u8>, Error = ()> {
future::poll_fn(move || {
if self
.message_ready
.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
let bytes: Vec<u8> = mem::replace(&mut self.buf.lock().unwrap(), vec![]);
Ok(Async::Ready(bytes))
} else {
task::current().notify();
Ok(Async::NotReady)
}
})
}
pub fn into_message(self) -> Message {
pub fn into_message(self) -> impl Future<Item = Message, Error = ()> {
let bytes = self.into_inner();
let mut decoder = BinDecoder::new(&bytes);
Message::read(&mut decoder).expect("could not decode message")
bytes.map(|b| {
let mut decoder = BinDecoder::new(&b);
Message::read(&mut decoder).expect("could not decode message")
})
}
}
impl ResponseHandler for TestResponseHandler {
fn send_response(self, response: MessageResponse) -> io::Result<()> {
fn send_response(&self, response: MessageResponse) -> io::Result<()> {
let buf = &mut self.buf.lock().unwrap();
buf.clear();
let mut encoder = BinEncoder::new(buf);
response
.destructive_emit(&mut encoder)
.expect("could not encode");
self.message_ready.store(true, Ordering::Release);
Ok(())
}
}
@ -133,14 +152,20 @@ impl Stream for TestClientStream {
src: src_addr,
};
dbg!("catalog handling request");
let response_handler = TestResponseHandler::new();
self.catalog
.lock()
.unwrap()
.handle_request(&request, response_handler.clone())
.handle_request(request, response_handler.clone())
.wait()
.unwrap();
let buf = response_handler.into_inner();
dbg!("catalog handled request");
let buf = response_handler.into_inner().wait().unwrap();
dbg!("catalog responded");
Ok(Async::Ready(Some(SerialMessage::new(buf, src_addr))))
}
// now we get to drop through to the receives...

View File

@ -1,3 +1,4 @@
extern crate futures;
extern crate trust_dns;
extern crate trust_dns_integration;
extern crate trust_dns_server;
@ -5,6 +6,8 @@ extern crate trust_dns_server;
use std::collections::*;
use std::net::*;
use futures::Future;
use trust_dns::op::*;
use trust_dns::rr::rdata::*;
use trust_dns::rr::*;
@ -141,9 +144,10 @@ fn test_catalog_lookup() {
let response_handler = TestResponseHandler::new();
catalog
.lookup(&question_req, None, response_handler.clone())
.lookup(question_req, None, response_handler.clone())
.wait()
.unwrap();
let result = response_handler.into_message();
let result = response_handler.into_message().wait().unwrap();
assert_eq!(result.response_code(), ResponseCode::NoError);
assert_eq!(result.message_type(), MessageType::Response);
@ -184,9 +188,10 @@ fn test_catalog_lookup() {
let response_handler = TestResponseHandler::new();
catalog
.lookup(&question_req, None, response_handler.clone())
.lookup(question_req, None, response_handler.clone())
.wait()
.unwrap();
let result = response_handler.into_message();
let result = response_handler.into_message().wait().unwrap();
assert_eq!(result.response_code(), ResponseCode::NoError);
assert_eq!(result.message_type(), MessageType::Response);
@ -223,9 +228,10 @@ fn test_catalog_nx_soa() {
let response_handler = TestResponseHandler::new();
catalog
.lookup(&question_req, None, response_handler.clone())
.lookup(question_req, None, response_handler.clone())
.wait()
.unwrap();
let result = response_handler.into_message();
let result = response_handler.into_message().wait().unwrap();
assert_eq!(result.response_code(), ResponseCode::NXDomain);
assert_eq!(result.message_type(), MessageType::Response);
@ -287,9 +293,10 @@ fn test_axfr() {
let response_handler = TestResponseHandler::new();
catalog
.lookup(&question_req, None, response_handler.clone())
.lookup(question_req, None, response_handler.clone())
.wait()
.expect("lookup failed");
let result = response_handler.into_message();
let result = response_handler.into_message().wait().unwrap();
let mut answers: Vec<Record> = result.answers().to_vec();
@ -406,9 +413,10 @@ fn test_axfr_refused() {
let response_handler = TestResponseHandler::new();
catalog
.lookup(&question_req, None, response_handler.clone())
.lookup(question_req, None, response_handler.clone())
.wait()
.expect("lookup failed");
let result = response_handler.into_message();
let result = response_handler.into_message().wait().unwrap();
assert_eq!(result.response_code(), ResponseCode::Refused);
assert!(result.answers().is_empty());

View File

@ -29,9 +29,9 @@ use trust_dns::error::ClientErrorKind;
use trust_dns::op::ResponseCode;
#[cfg(feature = "dnssec")]
use trust_dns::rr::dnssec::Signer;
use trust_dns::rr::{DNSClass, Name, RData, RecordSet, RecordType};
#[cfg(feature = "dnssec")]
use trust_dns::rr::Record;
use trust_dns::rr::{DNSClass, Name, RData, RecordSet, RecordType};
use trust_dns::tcp::TcpClientStream;
use trust_dns::udp::UdpClientStream;
use trust_dns_proto::error::ProtoError;
@ -45,6 +45,8 @@ use trust_dns_integration::{NeverReturnsClientStream, TestClientStream};
#[test]
fn test_query_nonet() {
// env_logger::init();
let authority = create_example();
let mut catalog = Catalog::new();
catalog.upsert(authority.origin().clone(), Box::new(authority));
@ -160,14 +162,12 @@ where
.query(name.clone(), DNSClass::IN, RecordType::A)
.map(move |response| {
println!("response records: {:?}", response);
assert!(
response
.queries()
.first()
.expect("expected query")
.name()
.eq_case(&name)
);
assert!(response
.queries()
.first()
.expect("expected query")
.name()
.eq_case(&name));
let record = &response.answers()[0];
assert_eq!(record.name(), &name);
@ -179,7 +179,8 @@ where
} else {
assert!(false);
}
}).map_err(|e| {
})
.map_err(|e| {
assert!(false, "query failed: {}", e);
}),
)
@ -683,7 +684,8 @@ fn test_delete_by_rdata() {
record1.name().clone(),
record1.dns_class(),
record1.rr_type(),
)).expect("query failed");
))
.expect("query failed");
assert_eq!(result.response_code(), ResponseCode::NoError);
assert_eq!(result.answers().len(), 1);
assert!(result.answers().iter().any(|rr| *rr == record1));
@ -756,7 +758,8 @@ fn test_delete_by_rdata_multi() {
record1.name().clone(),
record1.dns_class(),
record1.rr_type(),
)).expect("query failed");
))
.expect("query failed");
assert_eq!(result.response_code(), ResponseCode::NoError);
assert_eq!(result.answers().len(), 2);
assert!(!result.answers().iter().any(|rr| *rr == record1));

View File

@ -1,3 +1,4 @@
extern crate futures;
extern crate rusqlite;
extern crate trust_dns;
extern crate trust_dns_integration;
@ -7,6 +8,8 @@ use std::collections::BTreeMap;
use std::net::*;
use std::str::FromStr;
use futures::future::Future;
use rusqlite::*;
use trust_dns::op::*;
@ -14,7 +17,7 @@ use trust_dns::rr::dnssec::*;
use trust_dns::rr::rdata::*;
use trust_dns::rr::*;
use trust_dns_server::authority::{Authority, MessageRequest, ZoneType};
use trust_dns_server::authority::{Authority, ZoneType};
use trust_dns_server::store::sqlite::{Journal, SqliteAuthority};
use trust_dns_integration::authority::create_example;
@ -30,7 +33,10 @@ fn test_search() {
query.set_name(origin.clone().into());
let query = LowerQuery::from(query);
let result = example.search(&query, false, SupportedAlgorithms::new()).unwrap();
let result = example
.search(&query, false, SupportedAlgorithms::new())
.wait()
.unwrap();
if !result.is_empty() {
let record = result.iter().next().unwrap();
assert_eq!(record.rr_type(), RecordType::A);
@ -51,7 +57,10 @@ fn test_search_www() {
query.set_name(www_name.clone());
let query = LowerQuery::from(query);
let result = example.search(&query, false, SupportedAlgorithms::new()).unwrap();
let result = example
.search(&query, false, SupportedAlgorithms::new())
.wait()
.unwrap();
if !result.is_empty() {
let record = result.iter().next().unwrap();
assert_eq!(record.rr_type(), RecordType::A);
@ -67,7 +76,14 @@ fn test_authority() {
let authority: SqliteAuthority = create_example();
assert_eq!(
authority.soa().unwrap().iter().next().unwrap().dns_class(),
authority
.soa()
.wait()
.unwrap()
.iter()
.next()
.unwrap()
.dns_class(),
DNSClass::IN
);
@ -78,11 +94,13 @@ fn test_authority() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.was_empty());
let mut lookup: Vec<_> = authority
.ns(false, SupportedAlgorithms::new())
.wait()
.unwrap()
.iter()
.cloned()
@ -117,6 +135,7 @@ fn test_authority() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.was_empty());
@ -127,6 +146,7 @@ fn test_authority() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -156,6 +176,7 @@ fn test_authority() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.iter()
.next()
@ -174,6 +195,7 @@ fn test_authority() {
#[test]
fn test_authorize() {
use trust_dns::serialize::binary::{BinDecodable, BinEncodable};
use trust_dns_server::authority::MessageRequest;
let authority: SqliteAuthority = create_example();
@ -595,6 +617,7 @@ fn test_update() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -611,6 +634,7 @@ fn test_update() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.was_empty());
}
@ -635,6 +659,7 @@ fn test_update() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.iter()
.collect::<Vec<_>>(),
@ -662,6 +687,7 @@ fn test_update() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -704,6 +730,7 @@ fn test_update() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.was_empty());
}
@ -728,6 +755,7 @@ fn test_update() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -778,6 +806,7 @@ fn test_update() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -807,6 +836,7 @@ fn test_update() {
false,
SupportedAlgorithms::new()
)
.wait()
.unwrap()
.was_empty());
assert_eq!(serial + 6, authority.serial());
@ -817,12 +847,15 @@ fn test_update() {
fn test_zone_signing() {
let authority: SqliteAuthority = create_secure_example();
let results = authority.lookup(
&authority.origin(),
RecordType::AXFR,
true,
SupportedAlgorithms::all(),
).unwrap();
let results = authority
.lookup(
&authority.origin(),
RecordType::AXFR,
true,
SupportedAlgorithms::all(),
)
.wait()
.unwrap();
assert!(
results
@ -831,12 +864,15 @@ fn test_zone_signing() {
"must contain a DNSKEY"
);
let results = authority.lookup(
&authority.origin(),
RecordType::AXFR,
true,
SupportedAlgorithms::all(),
).unwrap();
let results = authority
.lookup(
&authority.origin(),
RecordType::AXFR,
true,
SupportedAlgorithms::all(),
)
.wait()
.unwrap();
for record in &results {
if record.rr_type() == RecordType::DNSSEC(DNSSECRecordType::RRSIG) {
@ -846,12 +882,15 @@ fn test_zone_signing() {
continue;
}
let mut inner_results = authority.lookup(
&authority.origin(),
RecordType::AXFR,
true,
SupportedAlgorithms::all(),
).unwrap();
let mut inner_results = authority
.lookup(
&authority.origin(),
RecordType::AXFR,
true,
SupportedAlgorithms::all(),
)
.wait()
.unwrap();
// validate all records have associated RRSIGs after signing
assert!(
@ -876,7 +915,10 @@ fn test_get_nsec() {
let authority: SqliteAuthority = create_secure_example();
let lower_name = LowerName::from(name.clone());
let results = authority.get_nsec_records(&lower_name, true, SupportedAlgorithms::all()).unwrap();
let results = authority
.get_nsec_records(&lower_name, true, SupportedAlgorithms::all())
.wait()
.unwrap();
for record in &results {
assert!(*record.name() < name);
@ -917,6 +959,7 @@ fn test_journal() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
@ -924,12 +967,15 @@ fn test_journal() {
assert!(new_rrset.iter().all(|r| *r == new_record));
let lower_delete_name = LowerName::from(delete_name.clone());
let delete_rrset = authority.lookup(
&lower_delete_name,
RecordType::A,
false,
SupportedAlgorithms::new(),
).unwrap();
let delete_rrset = authority
.lookup(
&lower_delete_name,
RecordType::A,
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap();
assert!(delete_rrset.was_empty());
// that record should have been recorded... let's reload the journal and see if we get it.
@ -953,18 +999,22 @@ fn test_journal() {
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap()
.iter()
.cloned()
.collect();
assert!(new_rrset.iter().all(|r| *r == new_record));
let delete_rrset = authority.lookup(
&lower_delete_name,
RecordType::A,
false,
SupportedAlgorithms::new(),
).unwrap();
let delete_rrset = authority
.lookup(
&lower_delete_name,
RecordType::A,
false,
SupportedAlgorithms::new(),
)
.wait()
.unwrap();
assert!(delete_rrset.was_empty());
}
@ -1000,9 +1050,10 @@ fn test_recovery() {
);
assert!(recovered_authority
.soa()
.wait()
.unwrap()
.iter()
.zip(authority.soa().unwrap().iter())
.zip(authority.soa().wait().unwrap().iter())
.all(|(r1, r2)| r1 == r2));
assert!(recovered_authority
.records()
@ -1047,7 +1098,10 @@ fn test_axfr() {
Name::from_str("example.com.").unwrap(),
RecordType::AXFR,
));
let result = authority.search(&query, false, SupportedAlgorithms::new()).unwrap();
let result = authority
.search(&query, false, SupportedAlgorithms::new())
.wait()
.unwrap();
// just update this if the count goes up in the authority
assert_eq!(result.iter().count(), 10);
@ -1062,7 +1116,9 @@ fn test_refused_axfr() {
Name::from_str("example.com.").unwrap(),
RecordType::AXFR,
));
let result = authority.search(&query, false, SupportedAlgorithms::new());
let result = authority
.search(&query, false, SupportedAlgorithms::new())
.wait();
// just update this if the count goes up in the authority
assert!(result.unwrap_err().is_refused());