Futurize the RequestHandler and use AsyncResolver
This commit is contained in:
parent
c06ea4df2c
commit
58b13f9e53
18
CHANGELOG.md
18
CHANGELOG.md
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>;
|
@ -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>;
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
29
crates/server/src/authority/zone_type.rs
Normal file
29
crates/server/src/authority/zone_type.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
crates/server/src/store/forwarder/config.rs
Normal file
17
crates/server/src/store/forwarder/config.rs
Normal 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>,
|
||||
}
|
@ -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;
|
||||
|
@ -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")]
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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" }] }
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
extern crate futures;
|
||||
extern crate trust_dns;
|
||||
extern crate trust_dns_server;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
extern crate futures;
|
||||
extern crate trust_dns;
|
||||
extern crate trust_dns_server;
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
);
|
||||
})
|
||||
}
|
||||
|
@ -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...
|
||||
|
@ -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());
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user