fix notify and create to take RecordSets and not single records
This commit is contained in:
parent
ab3e276b2b
commit
58e082a937
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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() }
|
||||
|
@ -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>)> {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
},
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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>,
|
||||
|
Loading…
Reference in New Issue
Block a user