fix notify and create to take RecordSets and not single records

This commit is contained in:
Benjamin Fry 2016-12-10 17:54:31 -08:00
parent ab3e276b2b
commit 58e082a937
14 changed files with 542 additions and 379 deletions

View File

@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- new ServerFuture tokio and futures based server, #61
- UdpStream & TcpSteam to support stream of messages with src address
- TimeoutStream to wrap TcpStreams to help guard against malicious clients
- Added Notify support to ClientFuture
### Changed
- Split Server and Client into separate crates, #43

View File

@ -22,7 +22,7 @@ use tokio_core::reactor::{Handle, Timeout};
use ::error::*;
use ::op::{Message, MessageType, OpCode, Query, UpdateMessage};
use ::rr::{domain, DNSClass, RData, Record, RecordType};
use ::rr::{domain, DNSClass, IntoRecordSet, RData, Record, RecordType};
use ::rr::dnssec::Signer;
use ::rr::rdata::NULL;
@ -376,8 +376,6 @@ pub trait ClientHandle: Clone {
self.send(message)
}
/// Send NOTIFY message
///
/// Sends a NOTIFY message to the remote system
///
/// [RFC 1996](https://tools.ietf.org/html/rfc1996), DNS NOTIFY, August 1996
@ -438,8 +436,8 @@ pub trait ClientHandle: Clone {
/// * `query_class` - most likely this should always be DNSClass::IN
/// * `query_type` - record type which has been updated
/// * `record` - the new version of the record being notified
fn notify(&mut self, name: domain::Name, query_class: DNSClass, query_type: RecordType, record: Option<Record>)
-> Box<Future<Item=Message, Error=ClientError>> {
fn notify<R>(&mut self, name: domain::Name, query_class: DNSClass, query_type: RecordType, record: Option<R>)
-> Box<Future<Item=Message, Error=ClientError>> where R: IntoRecordSet {
debug!("notifying: {} {:?}", name, query_type);
// build the message
@ -468,7 +466,7 @@ pub trait ClientHandle: Clone {
message.add_query(query);
// add the notify message, see https://tools.ietf.org/html/rfc1996, section 3.7
if let Some(record) = record { message.add_answer(record); }
if let Some(record) = record { message.add_answers(record.into_record_set()); }
self.send(message)
}
@ -506,25 +504,28 @@ pub trait ClientHandle: Clone {
/// * `zone_origin` - the zone name to update, i.e. SOA name
///
/// The update must go to a zone authority (i.e. the server used in the ClientConnection)
fn create(&mut self,
record: Record,
zone_origin: domain::Name)
-> Box<Future<Item=Message, Error=ClientError>> {
assert!(zone_origin.zone_of(record.get_name()));
fn create<R>(&mut self,
rrset: R,
zone_origin: domain::Name)
-> Box<Future<Item=Message, Error=ClientError>>
where R: IntoRecordSet {
// TODO: assert non-empty rrset?
let rrset = rrset.into_record_set();
assert!(zone_origin.zone_of(rrset.get_name()));
// for updates, the query section is used for the zone
let mut zone: Query = Query::new();
zone.name(zone_origin).query_class(record.get_dns_class()).query_type(RecordType::SOA);
zone.name(zone_origin).query_class(rrset.get_dns_class()).query_type(RecordType::SOA);
// build the message
let mut message: Message = Message::new();
message.id(rand::random()).message_type(MessageType::Query).op_code(OpCode::Update).recursion_desired(false);
message.add_zone(zone);
let mut prerequisite = Record::with(record.get_name().clone(), record.get_rr_type(), 0);
let mut prerequisite = Record::with(rrset.get_name().clone(), rrset.get_record_type(), 0);
prerequisite.dns_class(DNSClass::NONE);
message.add_pre_requisite(prerequisite);
message.add_update(record);
message.add_updates(rrset);
// Extended dns
{

View File

@ -123,6 +123,7 @@ impl Message {
/// it's unclear how it could be useful to have more than one here? change this to set
/// a single query?
pub fn add_query(&mut self, query: Query) -> &mut Self { self.queries.push(query); self }
#[deprecated = "will be removed post 0.9.x"]
pub fn add_all_queries(&mut self, queries: &[Query]) -> &mut Self {
for q in queries {
// TODO: the clone here should really be performed (or not) by the caller
@ -130,7 +131,18 @@ impl Message {
}
self
}
pub fn add_queries<Q, I>(&mut self, queries: Q) -> &mut Self
where Q: IntoIterator<Item=Query, IntoIter=I>,
I: Iterator<Item=Query> {
for query in queries {
self.add_query(query);
}
self
}
pub fn add_answer(&mut self, record: Record) -> &mut Self { self.answers.push(record); self }
#[deprecated = "will be removed post 0.9.x"]
pub fn add_all_answers(&mut self, vector: &[&Record]) -> &mut Self {
for &r in vector {
// TODO: in order to get rid of this clone, we need an owned Message for decoding, and a
@ -139,6 +151,15 @@ impl Message {
}
self
}
pub fn add_answers<R, I>(&mut self, records: R) -> &mut Self
where R: IntoIterator<Item=Record, IntoIter=I>,
I: Iterator<Item=Record> {
for record in records {
self.add_answer(record);
}
self
}
/// Sets the answers to the specified set of Records.
///
@ -151,6 +172,7 @@ impl Message {
}
pub fn add_name_server(&mut self, record: Record) -> &mut Self { self.name_servers.push(record); self }
#[deprecated = "will be removed post 0.9.x"]
pub fn add_all_name_servers(&mut self, vector: &[&Record]) -> &mut Self {
for &r in vector {
// TODO: in order to get rid of this clone, we need an owned Message for decoding, and a
@ -159,6 +181,15 @@ impl Message {
}
self
}
pub fn add_name_servers<R, I>(&mut self, records: R) -> &mut Self
where R: IntoIterator<Item=Record, IntoIter=I>,
I: Iterator<Item=Record> {
for record in records {
self.add_name_server(record);
}
self
}
/// Sets the name_servers to the specified set of Records.
///
@ -466,7 +497,11 @@ pub trait UpdateMessage: Debug {
fn add_pre_requisite(&mut self, record: Record);
fn add_all_pre_requisites(&mut self, vector: &[&Record]);
fn add_update(&mut self, record: Record);
#[deprecated = "will be removed post 0.9.x"]
fn add_all_updates(&mut self, vector: &[&Record]);
fn add_updates<R,I>(&mut self, records: R)
where R: IntoIterator<Item=Record, IntoIter=I>,
I: Iterator<Item=Record>;
fn add_additional(&mut self, record: Record);
fn get_zones(&self) -> &[Query];
@ -488,9 +523,14 @@ impl UpdateMessage for Message {
fn get_id(&self) -> u16 { self.get_id() }
fn add_zone(&mut self, query: Query) { self.add_query(query); }
fn add_pre_requisite(&mut self, record: Record) { self.add_answer(record); }
fn add_all_pre_requisites(&mut self, vector: &[&Record]) { self.add_all_answers(vector); }
fn add_all_pre_requisites(&mut self, vector: &[&Record]) { self.add_answers(vector.into_iter().map(|r| (*r).clone())); }
fn add_update(&mut self, record: Record) { self.add_name_server(record); }
fn add_all_updates(&mut self, vector: &[&Record]) { self.add_all_name_servers(vector); }
fn add_all_updates(&mut self, vector: &[&Record]) { self.add_name_servers(vector.into_iter().map(|r| (*r).clone())); }
fn add_updates<R,I>(&mut self, records: R)
where R: IntoIterator<Item=Record, IntoIter=I>,
I: Iterator<Item=Record> {
self.add_name_servers(records);
}
fn add_additional(&mut self, record: Record) { self.add_additional(record); }
fn get_zones(&self) -> &[Query] { self.get_queries() }

View File

@ -519,6 +519,7 @@ mod tests {
use std::cmp::Ordering;
use ::serialize::binary::bin_tests::{test_read_data_set, test_emit_data_set};
#[allow(unused)]
use ::serialize::binary::*;
fn get_data() -> Vec<(Name, Vec<u8>)> {

View File

@ -32,4 +32,8 @@ pub use self::record_data::RData;
pub use self::record_type::RecordType;
pub use self::resource::Record;
pub use self::rr_key::RrKey;
pub use self::rr_set::RrSet;
pub use self::rr_set::IntoRecordSet;
pub use self::rr_set::RecordSet;
#[deprecated = "will be removed post 0.9.x, use RecordSet"]
pub type RrSet = RecordSet;

View File

@ -826,6 +826,7 @@ mod tests {
use std::str::FromStr;
use super::*;
#[allow(unused)]
use ::serialize::binary::*;
use ::serialize::binary::bin_tests::test_emit_data_set;
use ::rr::domain::Name;

View File

@ -21,12 +21,13 @@ use std::cmp::Ordering;
use ::serialize::binary::*;
use ::error::*;
use ::rr::dns_class::DNSClass;
use ::rr::domain;
use ::rr::IntoRecordSet;
use ::rr::rdata::NULL;
use super::record_data::RData;
use super::record_type::RecordType;
use super::dns_class::DNSClass;
use super::domain;
use ::rr::RData;
use ::rr::RecordType;
use ::rr::RecordSet;
/// Resource records are storage value in DNS, into which all key/value pair data is stored.
///
@ -149,6 +150,12 @@ impl Record {
pub fn unwrap_rdata(self) -> RData { self.rdata }
}
impl IntoRecordSet for Record {
fn into_record_set(self) -> RecordSet {
RecordSet::from(self)
}
}
impl BinSerializable<Record> for Record {
/// parse a resource record line example:
/// WARNING: the record_bytes is 100% consumed and destroyed in this parsing process
@ -321,7 +328,7 @@ mod tests {
use std::cmp::Ordering;
use super::*;
#[allow(unused)]
use ::serialize::binary::*;
use ::rr::record_data::RData;
use ::rr::record_type::RecordType;

View File

@ -4,28 +4,31 @@
// 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 std::slice::Iter;
use std::iter::Chain;
use std::slice::Iter;
use std::vec;
use ::rr::{Name, Record, RecordType, RData};
use ::rr::{DNSClass, Name, Record, RecordType, RData};
/// Set of resource records associated to a name and type
#[derive(Debug, PartialEq)]
pub struct RrSet {
#[derive(Clone, Debug, PartialEq)]
pub struct RecordSet {
name: Name,
record_type: RecordType,
dns_class: DNSClass,
ttl: u32,
records: Vec<Record>,
rrsigs: Vec<Record>,
serial: u32, // serial number at which this record was modified
}
impl RrSet {
impl RecordSet {
/// Creates a new Resource Record Set.
///
/// # Arguments
///
/// * `name` - The label for the `RrSet`
/// * `record_type` - `RecordType` of this `RrSet`, all records in the `RrSet` must be of the
/// * `name` - The label for the `RecordSet`
/// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the
/// specified `RecordType`.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates.
@ -34,8 +37,30 @@ impl RrSet {
///
/// The newly created Resource Record Set
/// TODO: make all cloned params pass by value
pub fn new(name: &Name, record_type: RecordType, serial: u32) -> RrSet {
RrSet{name: name.clone(), record_type: record_type, ttl: 0, records: Vec::new(), rrsigs: Vec::new(), serial: serial}
pub fn new(name: &Name, record_type: RecordType, serial: u32) -> Self {
RecordSet{name: name.clone(), record_type: record_type, dns_class: DNSClass::IN,
ttl: 0, records: Vec::new(), rrsigs: Vec::new(), serial: serial}
}
/// Creates a new Resource Record Set from a Record
///
/// # Arguments
///
/// * `record` - initializes a record set with a single record
///
/// # Return value
///
/// The newly created Resource Record Set
pub fn from(record: Record) -> Self {
RecordSet {
name: record.get_name().clone(),
record_type: record.get_rr_type(),
dns_class: record.get_dns_class(),
ttl: record.get_ttl(),
records: vec![record],
rrsigs: vec![],
serial: 0,
}
}
/// # Return value
@ -52,10 +77,17 @@ impl RrSet {
self.record_type
}
/// # Return value
///
/// `DNSClass` of the RecordSet
pub fn get_dns_class(&self) -> DNSClass {
self.dns_class
}
/// # Return value
///
/// TTL, time-to-live, of the Resource Record Set, this is the maximum length of time that an
/// RrSet should be cached.
/// RecordSet should be cached.
pub fn get_ttl(&self) -> u32 {
self.ttl
}
@ -130,7 +162,7 @@ impl RrSet {
///
/// # Arguments
///
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RrSet`.
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates. The serial will only be updated if the
/// record was added.
@ -138,6 +170,8 @@ impl RrSet {
/// # Return value
///
/// True if the record was inserted.
///
/// FIXME: make a default add without serial number for basic usage
pub fn insert(&mut self, record: Record, serial: u32) -> bool {
assert_eq!(record.get_name(), &self.name);
assert_eq!(record.get_rr_type(), self.record_type);
@ -178,7 +212,7 @@ impl RrSet {
// CNAME compare only NAME, CLASS, and TYPE -- it is not possible
// to have more than one CNAME RR, even if their data fields
// differ.
RecordType::CNAME => {
RecordType::CNAME => {
assert!(self.records.len() <= 1);
self.records.clear();
},
@ -220,7 +254,7 @@ impl RrSet {
///
/// # Arguments
///
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RrSet`. Removes
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`. Removes
/// any `record` if the record data, `RData`, match.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates. The serial will only be updated if the
@ -266,6 +300,23 @@ impl RrSet {
}
}
pub trait IntoRecordSet: Sized {
fn into_record_set(self) -> RecordSet;
}
impl IntoRecordSet for RecordSet {
fn into_record_set(self) -> Self { self }
}
impl IntoIterator for RecordSet {
type Item = Record;
type IntoIter = Chain<vec::IntoIter<Record>, vec::IntoIter<Record>>;
fn into_iter(self) -> Self::IntoIter {
self.records.into_iter().chain(self.rrsigs.into_iter())
}
}
#[cfg(test)]
mod test {
use std::net::Ipv4Addr;
@ -276,7 +327,7 @@ mod test {
fn test_insert() {
let name = Name::new().label("www").label("example").label("com");
let record_type = RecordType::A;
let mut rr_set = RrSet::new(&name, record_type, 0);
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone();
@ -301,7 +352,7 @@ mod test {
fn test_insert_soa() {
let name = Name::new().label("example").label("com");
let record_type = RecordType::SOA;
let mut rr_set = RrSet::new(&name, record_type, 0);
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
let same_serial = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.net.", None).unwrap(), Name::parse("noc.dns.icann.net.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
@ -330,7 +381,7 @@ mod test {
let new_cname = Name::new().label("w2").label("example").label("com");
let record_type = RecordType::CNAME;
let mut rr_set = RrSet::new(&name, record_type, 0);
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::CNAME).dns_class(DNSClass::IN).rdata(RData::CNAME(cname.clone()) ).clone();
let new_record = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::CNAME).dns_class(DNSClass::IN).rdata(RData::CNAME(new_cname.clone()) ).clone();
@ -348,7 +399,7 @@ mod test {
fn test_remove() {
let name = Name::new().label("www").label("example").label("com");
let record_type = RecordType::A;
let mut rr_set = RrSet::new(&name, record_type, 0);
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone();
let insert1 = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,25))).clone();
@ -366,7 +417,7 @@ mod test {
fn test_remove_soa() {
let name = Name::new().label("example").label("com");
let record_type = RecordType::SOA;
let mut rr_set = RrSet::new(&name, record_type, 0);
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
@ -379,7 +430,7 @@ mod test {
fn test_remove_ns() {
let name = Name::new().label("example").label("com");
let record_type = RecordType::NS;
let mut rr_set = RrSet::new(&name, record_type, 0);
let mut rr_set = RecordSet::new(&name, record_type, 0);
let ns1 = Record::new().name(name.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone();
let ns2 = Record::new().name(name.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone();

View File

@ -16,7 +16,7 @@
use std::collections::BTreeMap;
use ::error::*;
use ::rr::{ Name, RecordType, Record, DNSClass, RData, RrKey, RrSet};
use ::rr::{ Name, IntoRecordSet, RecordType, Record, DNSClass, RData, RrKey, RecordSet };
use super::master_lex::{Lexer, Token};
@ -128,9 +128,9 @@ impl Parser {
}
// TODO: change this function to load into an Authority, using the update_records() method
pub fn parse(&mut self, lexer: Lexer, origin: Option<Name>) -> ParseResult<(Name, BTreeMap<RrKey, RrSet>)> {
pub fn parse(&mut self, lexer: Lexer, origin: Option<Name>) -> ParseResult<(Name, BTreeMap<RrKey, RecordSet>)> {
let mut lexer = lexer;
let mut records: BTreeMap<RrKey, RrSet> = BTreeMap::new();
let mut records: BTreeMap<RrKey, RecordSet> = BTreeMap::new();
let mut origin: Option<Name> = origin;
let mut current_name: Option<Name> = None;
@ -269,15 +269,14 @@ impl Parser {
match rtype.unwrap() {
RecordType::SOA => {
let mut set = RrSet::new(record.get_name(), record.get_rr_type(), 0);
set.insert(record, 0);
let set = record.into_record_set();
if records.insert(key, set).is_some() {
return Err(ParseErrorKind::Message("SOA is already specified").into());
}
},
_ => {
// add a Vec if it's not there, then add the record to the list
let mut set = records.entry(key).or_insert(RrSet::new(record.get_name(), record.get_rr_type(), 0));
let mut set = records.entry(key).or_insert(RecordSet::new(record.get_name(), record.get_rr_type(), 0));
set.insert(record, 0);
},
}

View File

@ -22,7 +22,7 @@ use trust_dns::client::{ClientFuture, BasicClientHandle, ClientHandle, ClientStr
use trust_dns::error::*;
use trust_dns::op::ResponseCode;
use trust_dns::rr::domain;
use trust_dns::rr::{DNSClass, RData, Record, RecordType};
use trust_dns::rr::{DNSClass, IntoRecordSet, RData, Record, RecordType, RecordSet};
use trust_dns::rr::dnssec::{Algorithm, Signer};
use trust_dns::rr::rdata::*;
use trust_dns::udp::UdpClientStream;
@ -116,25 +116,43 @@ fn test_query(client: &mut BasicClientHandle) -> Box<Future<Item=(), Error=()>>
let name = domain::Name::with_labels(vec!["WWW".to_string(), "example".to_string(), "com".to_string()]);
Box::new(client.query(name.clone(), DNSClass::IN, RecordType::A)
.map(move |response| {
println!("response records: {:?}", response);
assert_eq!(response.get_queries().first().expect("expected query").get_name().cmp_with_case(&name, false), Ordering::Equal);
.map(move |response| {
println!("response records: {:?}", response);
assert_eq!(response.get_queries().first().expect("expected query").get_name().cmp_with_case(&name, false), Ordering::Equal);
let record = &response.get_answers()[0];
assert_eq!(record.get_name(), &name);
assert_eq!(record.get_rr_type(), RecordType::A);
assert_eq!(record.get_dns_class(), DNSClass::IN);
let record = &response.get_answers()[0];
assert_eq!(record.get_name(), &name);
assert_eq!(record.get_rr_type(), RecordType::A);
assert_eq!(record.get_dns_class(), DNSClass::IN);
if let &RData::A(ref address) = record.get_rdata() {
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
} else {
assert!(false);
}
})
.map_err(|e| {
assert!(false, "query failed: {}", e);
})
)
if let &RData::A(ref address) = record.get_rdata() {
assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
} else {
assert!(false);
}
})
.map_err(|e| {
assert!(false, "query failed: {}", e);
})
)
}
#[test]
fn test_notify() {
let authority = create_example();
let mut catalog = Catalog::new();
catalog.upsert(authority.get_origin().clone(), authority);
let mut io_loop = Core::new().unwrap();
let (stream, sender) = TestClientStream::new(catalog);
let mut client = ClientFuture::new(stream, sender, io_loop.handle(), None);
let name = domain::Name::with_labels(vec!["ping".to_string(), "example".to_string(), "com".to_string()]);
let message = io_loop.run(client.notify(name.clone(), DNSClass::IN, RecordType::A, None::<RecordSet>));
assert!(message.is_ok());
let message = message.unwrap();
assert_eq!(message.get_response_code(), ResponseCode::NotImp, "the catalog must support Notify now, update this");
}
// update tests
@ -149,313 +167,350 @@ fn create_sig0_ready_client(io_loop: &Core) -> (BasicClientHandle, domain::Name)
let rsa = RSA::generate(512).unwrap();
let signer = Signer::new(Algorithm::RSASHA256,
rsa,
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
Duration::max_value());
rsa,
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
Duration::max_value());
// insert the KEY for the trusted.example.com
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
RecordType::KEY,
Duration::minutes(5).num_seconds() as u32);
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
authority.upsert(auth_key, 0);
// insert the KEY for the trusted.example.com
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
RecordType::KEY,
Duration::minutes(5).num_seconds() as u32);
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
authority.upsert(auth_key, 0);
// setup the catalog
let mut catalog = Catalog::new();
catalog.upsert(authority.get_origin().clone(), authority);
// setup the catalog
let mut catalog = Catalog::new();
catalog.upsert(authority.get_origin().clone(), authority);
let (stream, sender) = TestClientStream::new(catalog);
let client = ClientFuture::new(stream, sender, io_loop.handle(), Some(signer));
let (stream, sender) = TestClientStream::new(catalog);
let client = ClientFuture::new(stream, sender, io_loop.handle(), Some(signer));
(client, origin)
(client, origin)
}
#[test]
fn test_create() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// create a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert_eq!(result.get_answers()[0], record);
// trying to create again should error
// TODO: it would be cool to make this
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
// will fail if already set and not the same value.
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
}
#[test]
fn test_create_multiple() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// create a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
let mut rrset = record.clone().into_record_set();
let mut record2 = record.clone();
record2.rdata(RData::A(Ipv4Addr::new(100,10,100,11)));
rrset.insert(record2.clone(), 0);
let result = io_loop.run(client.create(rrset.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 2);
assert_eq!(result.get_answers()[0], record);
assert_eq!(result.get_answers()[1], record2);
// trying to create again should error
// TODO: it would be cool to make this
let result = io_loop.run(client.create(rrset, origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
// will fail if already set and not the same value.
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,12)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
}
#[test]
fn test_append() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("append failed");
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
// next append to a non-existent RRset
let result = io_loop.run(client.append(record.clone(), origin.clone(), false)).expect("append failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert_eq!(result.get_answers()[0], record);
// will fail if already set and not the same value.
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 2);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
// show that appending the same thing again is ok, but doesn't add any records
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 2);
}
#[test]
fn test_compare_and_swap() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// create a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let current = record;
let mut new = current.clone();
new.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.compare_and_swap(current.clone(), new.clone(), origin.clone())).expect("compare_and_swap failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
// check the it fails if tried again.
let mut new = new;
new.rdata(RData::A(Ipv4Addr::new(102,12,102,12)));
let result = io_loop.run(client.compare_and_swap(current, new.clone(), origin.clone())).expect("compare_and_swap failed");
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
}
#[test]
fn test_delete_by_rdata() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// next create to a non-existent RRset
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
}
#[test]
fn test_delete_rrset() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// next create to a non-existent RRset
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
assert_eq!(result.get_answers().len(), 0);
}
#[test]
fn test_delete_all() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// next create to a non-existent RRset
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let mut record = record.clone();
record.rr_type(RecordType::AAAA);
record.rdata(RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::A)).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
assert_eq!(result.get_answers().len(), 0);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::AAAA)).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
assert_eq!(result.get_answers().len(), 0);
}
// need to do something with the message channel, otherwise the ClientFuture will think there
// is no one listening to messages and shutdown...
#[allow(dead_code)]
pub struct NeverReturnsClientStream {
outbound_messages: Fuse<UnboundedReceiver<Vec<u8>>>,
}
impl NeverReturnsClientStream {
pub fn new() -> (Box<Future<Item=Self, Error=io::Error>>, Box<ClientStreamHandle>) {
let (message_sender, outbound_messages) = unbounded();
let stream: Box<Future<Item=NeverReturnsClientStream, Error=io::Error>> = Box::new(finished(
NeverReturnsClientStream {
outbound_messages: outbound_messages.fuse()
}
));
(stream, Box::new(message_sender))
}
}
impl Stream for NeverReturnsClientStream {
type Item = Vec<u8>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
// always not ready...
park().unpark();
Ok(Async::NotReady)
}
}
impl fmt::Debug for NeverReturnsClientStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TestClientStream catalog")
}
}
#[test]
fn test_timeout_query_nonet() {
let authority = create_example();
let mut catalog = Catalog::new();
catalog.upsert(authority.get_origin().clone(), authority);
let mut io_loop = Core::new().unwrap();
let (stream, sender) = NeverReturnsClientStream::new();
let mut client = ClientFuture::with_timeout(stream, sender, io_loop.handle(),
std::time::Duration::from_millis(1), None);
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).unwrap_err().kind() {
()
} else {
assert!(false);
}
#[test]
fn test_create() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// create a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert_eq!(result.get_answers()[0], record);
// trying to create again should error
// TODO: it would be cool to make this
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
// will fail if already set and not the same value.
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::YXRRSet);
}
#[test]
fn test_append() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("append failed");
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
// next append to a non-existent RRset
let result = io_loop.run(client.append(record.clone(), origin.clone(), false)).expect("append failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert_eq!(result.get_answers()[0], record);
// will fail if already set and not the same value.
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 2);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
// show that appending the same thing again is ok, but doesn't add any records
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 2);
}
#[test]
fn test_compare_and_swap() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// create a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let current = record;
let mut new = current.clone();
new.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.compare_and_swap(current.clone(), new.clone(), origin.clone())).expect("compare_and_swap failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
// check the it fails if tried again.
let mut new = new;
new.rdata(RData::A(Ipv4Addr::new(102,12,102,12)));
let result = io_loop.run(client.compare_and_swap(current, new.clone(), origin.clone())).expect("compare_and_swap failed");
assert_eq!(result.get_response_code(), ResponseCode::NXRRSet);
let result = io_loop.run(client.query(new.get_name().clone(), new.get_dns_class(), new.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(101,11,101,11) } else { false }));
}
#[test]
fn test_delete_by_rdata() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// next create to a non-existent RRset
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.delete_by_rdata(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert!(result.get_answers().iter().any(|rr| if let &RData::A(ref ip) = rr.get_rdata() { *ip == Ipv4Addr::new(100,10,100,10) } else { false }));
}
#[test]
fn test_delete_rrset() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// next create to a non-existent RRset
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let mut record = record.clone();
record.rdata(RData::A(Ipv4Addr::new(101,11,101,11)));
let result = io_loop.run(client.append(record.clone(), origin.clone(), true)).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.delete_rrset(record.clone(), origin.clone())).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), record.get_rr_type())).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
assert_eq!(result.get_answers().len(), 0);
}
#[test]
fn test_delete_all() {
let mut io_loop = Core::new().unwrap();
let (mut client, origin) = create_sig0_ready_client(&io_loop);
// append a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
// first check the must_exist option
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// next create to a non-existent RRset
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let mut record = record.clone();
record.rr_type(RecordType::AAAA);
record.rdata(RData::AAAA(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)));
let result = io_loop.run(client.create(record.clone(), origin.clone())).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
// verify record contents
let result = io_loop.run(client.delete_all(record.get_name().clone(), origin.clone(), DNSClass::IN)).expect("delete failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::A)).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
assert_eq!(result.get_answers().len(), 0);
let result = io_loop.run(client.query(record.get_name().clone(), record.get_dns_class(), RecordType::AAAA)).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NXDomain);
assert_eq!(result.get_answers().len(), 0);
}
// need to do something with the message channel, otherwise the ClientFuture will think there
// is no one listening to messages and shutdown...
#[allow(dead_code)]
pub struct NeverReturnsClientStream {
outbound_messages: Fuse<UnboundedReceiver<Vec<u8>>>,
}
impl NeverReturnsClientStream {
pub fn new() -> (Box<Future<Item=Self, Error=io::Error>>, Box<ClientStreamHandle>) {
let (message_sender, outbound_messages) = unbounded();
let stream: Box<Future<Item=NeverReturnsClientStream, Error=io::Error>> = Box::new(finished(
NeverReturnsClientStream {
outbound_messages: outbound_messages.fuse()
}
));
(stream, Box::new(message_sender))
}
}
impl Stream for NeverReturnsClientStream {
type Item = Vec<u8>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
// always not ready...
park().unpark();
Ok(Async::NotReady)
}
}
impl fmt::Debug for NeverReturnsClientStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TestClientStream catalog")
}
}
#[test]
fn test_timeout_query_nonet() {
let authority = create_example();
let mut catalog = Catalog::new();
catalog.upsert(authority.get_origin().clone(), authority);
let mut io_loop = Core::new().unwrap();
let (stream, sender) = NeverReturnsClientStream::new();
let mut client = ClientFuture::with_timeout(stream, sender, io_loop.handle(),
std::time::Duration::from_millis(1), None);
let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A)).unwrap_err().kind() {
()
} else {
assert!(false);
}
// test that we don't have any thing funky with registering new timeouts, etc...
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::AAAA)).unwrap_err().kind() {
()
} else {
assert!(false);
}
// test that we don't have any thing funky with registering new timeouts, etc...
if let &ClientErrorKind::Timeout = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::AAAA)).unwrap_err().kind() {
()
} else {
assert!(false);
}
}

View File

@ -192,6 +192,7 @@ fn bind_process() -> (NamedProcess, u16) {
}
#[bench]
#[ignore]
fn bind_udp_bench(b: &mut Bencher) {
let (named, server_port) = bind_process();
@ -207,6 +208,7 @@ fn bind_udp_bench(b: &mut Bencher) {
}
#[bench]
#[ignore]
fn bind_tcp_bench(b: &mut Bencher) {
let (named, server_port) = bind_process();

View File

@ -18,7 +18,7 @@ use std::collections::BTreeMap;
use chrono::UTC;
use trust_dns::op::{Message, UpdateMessage, ResponseCode, Query};
use trust_dns::rr::{DNSClass, Name, RData, Record, RecordType, RrKey, RrSet};
use trust_dns::rr::{DNSClass, Name, RData, Record, RecordType, RrKey, RecordSet};
use trust_dns::rr::rdata::{NSEC, SIG};
use trust_dns::rr::dnssec::Signer;
@ -34,7 +34,7 @@ pub struct Authority {
origin: Name,
class: DNSClass,
journal: Option<Journal>,
records: BTreeMap<RrKey, RrSet>,
records: BTreeMap<RrKey, RecordSet>,
zone_type: ZoneType,
allow_update: bool,
// Private key mapped to the Record of the DNSKey
@ -59,7 +59,7 @@ impl Authority {
/// # Return value
///
/// The new `Authority`.
pub fn new(origin: Name, records: BTreeMap<RrKey, RrSet>, zone_type: ZoneType, allow_update: bool) -> Authority {
pub fn new(origin: Name, records: BTreeMap<RrKey, RecordSet>, zone_type: ZoneType, allow_update: bool) -> Authority {
Authority{ origin: origin, class: DNSClass::IN, journal: None, records: records, zone_type: zone_type,
allow_update: allow_update, secure_keys: Vec::new() }
}
@ -163,7 +163,7 @@ impl Authority {
}
/// Get all the
pub fn get_records(&self) -> &BTreeMap<RrKey, RrSet> {
pub fn get_records(&self) -> &BTreeMap<RrKey, RecordSet> {
&self.records
}
@ -727,7 +727,7 @@ impl Authority {
assert_eq!(self.class, record.get_dns_class());
let rr_key = RrKey::new(record.get_name(), record.get_rr_type());
let records: &mut RrSet = self.records.entry(rr_key).or_insert(RrSet::new(record.get_name(), record.get_rr_type(), serial));
let records: &mut RecordSet = self.records.entry(rr_key).or_insert(RecordSet::new(record.get_name(), record.get_rr_type(), serial));
records.insert(record, serial)
}

View File

@ -225,7 +225,7 @@ impl Catalog {
response.id(request.get_id());
response.op_code(OpCode::Query);
response.message_type(MessageType::Response);
response.add_all_queries(request.get_queries());
response.add_queries(request.get_queries().into_iter().cloned());
// 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.
@ -239,18 +239,19 @@ impl Catalog {
if !records.is_empty() {
response.response_code(ResponseCode::NoError);
response.authoritative(true);
response.add_all_answers(&records);
response.add_answers(records.into_iter().cloned());
// get the NS records
let ns = authority.get_ns(is_dnssec);
if ns.is_empty() { warn!("there are no NS records for: {:?}", authority.get_origin()); }
else {
response.add_all_name_servers(&ns);
response.add_name_servers(ns.into_iter().cloned());
}
} else {
if is_dnssec {
// get NSEC records
response.add_all_name_servers(&authority.get_nsec_records(query.get_name(), is_dnssec));
let nsecs = authority.get_nsec_records(query.get_name(), is_dnssec);
response.add_name_servers(nsecs.into_iter().cloned());
}
// in the not found case it's standard to return the SOA in the authority section
@ -259,7 +260,7 @@ impl Catalog {
let soa = authority.get_soa_secure(is_dnssec);
if soa.is_empty() { warn!("there is no SOA record for: {:?}", authority.get_origin()); }
else {
response.add_all_name_servers(&soa);
response.add_name_servers(soa.into_iter().cloned());
}
}
} else {

View File

@ -31,7 +31,7 @@ use trust_dns::udp::{UdpHandler, UdpState};
use ::authority::Catalog;
// TODO, might be cool to store buffers for later usage...
#[deprecated]
#[deprecated = "will be removed post 0.9.x"]
pub struct Server {
handlers: HashMap<Token, DnsHandlerType>,
next_token: Cell<usize>,