allow non-FQDN in prep for search domains

This commit is contained in:
Benjamin Fry
2017-07-27 13:43:41 -07:00
parent 026bd28aeb
commit dee0a33c5e
3 changed files with 373 additions and 154 deletions

View File

@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## 0.11.0
### Added
- `Name::FromStr` for simpler parsing, specify trailing `.` for FQDN
### Changed
- *breaking* All `ClientHandle` traits now take `&Handle` instead of `Handle` (@rushmorem)
- *warning* `Name` now tracks if it is a fully qualified domain name, slightly changes name parsing rules, allowing `www.example.com` without the trailing `.`, which means that FQDN names are not enforced.
### Removed
- deprecated `Name::with_labels` see `Name::from_labels`
## 0.10.5 ## 0.10.5
### Added ### Added

View File

@@ -33,18 +33,24 @@ use error::*;
/// TODO: Currently this probably doesn't support binary names, it would be nice to do that. /// TODO: Currently this probably doesn't support binary names, it would be nice to do that.
#[derive(Debug, Eq, Clone)] #[derive(Debug, Eq, Clone)]
pub struct Name { pub struct Name {
is_fqdn: bool,
labels: Rc<Vec<Rc<String>>>, labels: Rc<Vec<Rc<String>>>,
} }
impl Name { impl Name {
/// Create a new domain::Name, i.e. label /// Create a new domain::Name, i.e. label
pub fn new() -> Self { pub fn new() -> Self {
Name { labels: Rc::new(Vec::new()) } Name {
is_fqdn: false,
labels: Rc::new(Vec::new()),
}
} }
/// Returns the root label, i.e. no labels, can probably make this better in the future. /// Returns the root label, i.e. no labels, can probably make this better in the future.
pub fn root() -> Self { pub fn root() -> Self {
Self::new() let mut this = Self::new();
this.is_fqdn = true;
this
} }
/// Returns true if there are no labels, i.e. it's empty. /// Returns true if there are no labels, i.e. it's empty.
@@ -63,6 +69,32 @@ impl Name {
self.labels.is_empty() self.labels.is_empty()
} }
/// Returns true if the name is a fully qualified domain name.
///
/// # Examples
///
/// ```
/// use std::str::FromStr;
/// use trust_dns::rr::domain::Name;
///
/// let name = Name::from_str("www").unwrap();
/// assert!(!name.is_fqdn());
///
/// let name = Name::from_str("www.example.com").unwrap();
/// assert!(!name.is_fqdn());
///
/// let name = Name::from_str("www.example.com.").unwrap();
/// assert!(name.is_fqdn());
/// ```
pub fn is_fqdn(&self) -> bool {
self.is_fqdn
}
/// Specifies this name is a fully qualified domain name
pub fn set_fqdn(&mut self, val: bool) {
self.is_fqdn = val
}
/// inline builder /// inline builder
pub fn label(mut self, label: &'static str) -> Self { pub fn label(mut self, label: &'static str) -> Self {
// TODO get_mut() on Arc was unstable when this was written // TODO get_mut() on Arc was unstable when this was written
@@ -86,14 +118,17 @@ impl Name {
/// use trust_dns::rr::domain::Name; /// use trust_dns::rr::domain::Name;
/// ///
/// let from_labels = Name::from_labels(vec!["www", "example", "com"]); /// let from_labels = Name::from_labels(vec!["www", "example", "com"]);
/// assert_eq!(from_labels, Name::from_str("www.example.com.").unwrap()); /// assert_eq!(from_labels, Name::from_str("www.example.com").unwrap());
/// ///
/// let root = Name::from_labels::<String>(vec![]); /// let root = Name::from_labels::<String>(vec![]);
/// assert!(root.is_root()); /// assert!(root.is_root());
/// ``` /// ```
pub fn from_labels<S: Into<String>>(labels: Vec<S>) -> Self { pub fn from_labels<S: Into<String>>(labels: Vec<S>) -> Self {
assert!(labels.len() < 256); // this should be an error assert!(labels.len() < 256); // this should be an error
Name { labels: Rc::new(labels.into_iter().map(|s| Rc::new(s.into())).collect()) } Name {
is_fqdn: false,
labels: Rc::new(labels.into_iter().map(|s| Rc::new(s.into())).collect()),
}
} }
/// Deprecated in favor of `from_labels` /// Deprecated in favor of `from_labels`
@@ -112,7 +147,10 @@ impl Name {
} }
assert!(new_labels.len() < 256); // this should be an error assert!(new_labels.len() < 256); // this should be an error
Name { labels: Rc::new(new_labels) } Name {
is_fqdn: self.is_fqdn,
labels: Rc::new(new_labels),
}
} }
/// appends the String to this label at the end /// appends the String to this label at the end
@@ -145,13 +183,15 @@ impl Name {
/// let example_com = Name::new().label("Example").label("Com"); /// let example_com = Name::new().label("Example").label("Com");
/// assert_eq!(example_com.to_lowercase().cmp_with_case(&Name::new().label("example").label("com"), false), Ordering::Equal); /// assert_eq!(example_com.to_lowercase().cmp_with_case(&Name::new().label("example").label("com"), false), Ordering::Equal);
/// ``` /// ```
pub fn to_lowercase(&self) -> Name { pub fn to_lowercase(&self) -> Self {
let mut new_labels = Vec::with_capacity(self.labels.len()); let mut new_labels = Vec::with_capacity(self.labels.len());
for label in self.labels.iter() { for label in self.labels.iter() {
new_labels.push(label.to_lowercase()); new_labels.push(label.to_lowercase());
} }
Self::from_labels(new_labels) let mut this = Self::from_labels(new_labels);
this.is_fqdn = self.is_fqdn;
this
} }
/// Trims off the first part of the name, to help with searching for the domain piece /// Trims off the first part of the name, to help with searching for the domain piece
@@ -190,7 +230,10 @@ impl Name {
pub fn trim_to(&self, num_labels: usize) -> Name { pub fn trim_to(&self, num_labels: usize) -> Name {
if self.labels.len() >= num_labels { if self.labels.len() >= num_labels {
let trim = self.labels.len() - num_labels; let trim = self.labels.len() - num_labels;
Name { labels: Rc::new(self.labels[trim..].to_vec()) } Name {
is_fqdn: self.is_fqdn,
labels: Rc::new(self.labels[trim..].to_vec()),
}
} else { } else {
Self::root() Self::root()
} }
@@ -282,6 +325,7 @@ impl Name {
// short cirtuit root parse // short cirtuit root parse
if local == "." { if local == "." {
name.set_fqdn(true);
return Ok(name); return Ok(name);
} }
@@ -363,10 +407,14 @@ impl Name {
// } // }
// } // }
if !local.ends_with('.') { // FIXME: mark as FQDN or not_FQDN
name.append(try!(origin.ok_or(ParseError::from( if local.ends_with('.') {
ParseErrorKind::Message("$ORIGIN was not specified"), name.set_fqdn(true);
)))); } else {
if let Some(other) = origin {
name.append(other);
name.set_fqdn(true);
}
} }
Ok(name) Ok(name)
@@ -628,7 +676,10 @@ impl BinSerializable<Name> for Name {
} }
} }
Ok(Name { labels: Rc::new(labels) }) Ok(Name {
is_fqdn: true,
labels: Rc::new(labels),
})
} }
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult { fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult {
@@ -738,7 +789,7 @@ mod tests {
} }
#[test] #[test]
fn num_labels() { fn test_num_labels() {
assert_eq!(Name::new().label("*").num_labels(), 0); assert_eq!(Name::new().label("*").num_labels(), 0);
assert_eq!(Name::new().label("a").num_labels(), 1); assert_eq!(Name::new().label("a").num_labels(), 1);
assert_eq!(Name::new().label("*").label("b").num_labels(), 1); assert_eq!(Name::new().label("*").label("b").num_labels(), 1);
@@ -748,12 +799,12 @@ mod tests {
} }
#[test] #[test]
fn parse() { fn test_read() {
test_read_data_set(get_data(), |ref mut d| Name::read(d)); test_read_data_set(get_data(), |ref mut d| Name::read(d));
} }
#[test] #[test]
fn write_to() { fn test_write_to() {
test_emit_data_set(get_data(), |e, n| n.emit(e)); test_emit_data_set(get_data(), |e, n| n.emit(e));
} }
@@ -843,7 +894,7 @@ mod tests {
#[test] #[test]
fn test_partial_cmp_eq() { fn test_partial_cmp_eq() {
let root = Some(Name::with_labels(vec![])); let root = Some(Name::from_labels(Vec::<String>::new()));
let comparisons: Vec<(Name, Name)> = vec![ let comparisons: Vec<(Name, Name)> = vec![
(root.clone().unwrap(), root.clone().unwrap()), (root.clone().unwrap(), root.clone().unwrap()),
( (
@@ -860,7 +911,7 @@ mod tests {
#[test] #[test]
fn test_partial_cmp() { fn test_partial_cmp() {
let root = Some(Name::with_labels(vec![])); let root = Some(Name::from_labels(Vec::<String>::new()));
let comparisons: Vec<(Name, Name)> = let comparisons: Vec<(Name, Name)> =
vec![ vec![
( (
@@ -905,7 +956,7 @@ mod tests {
#[test] #[test]
fn test_cmp_ignore_case() { fn test_cmp_ignore_case() {
let root = Some(Name::with_labels(vec![])); let root = Some(Name::from_labels(Vec::<String>::new()));
let comparisons: Vec<(Name, Name)> = vec![ let comparisons: Vec<(Name, Name)> = vec![
( (
Name::parse("ExAmPle", root.as_ref()).unwrap(), Name::parse("ExAmPle", root.as_ref()).unwrap(),
@@ -985,6 +1036,19 @@ mod tests {
Name::from_str("www.example.com.").unwrap(), Name::from_str("www.example.com.").unwrap(),
Name::from_labels(vec!["www", "example", "com"]) Name::from_labels(vec!["www", "example", "com"])
); );
assert_eq!(Name::from_str(".").unwrap(), Name::with_labels(vec![])); assert_eq!(Name::from_str(".").unwrap(), Name::from_labels(Vec::<String>::new()));
}
#[test]
fn test_fqdn() {
assert!(Name::root().is_fqdn());
assert!(Name::from_str(".").unwrap().is_fqdn());
assert!(Name::from_str("www.example.com.").unwrap().is_fqdn());
assert!(!Name::new().is_fqdn());
assert!(!Name::from_labels(vec!["www", "example", "com"]).is_fqdn());
assert!(!Name::from_str("www.example.com").unwrap().is_fqdn());
assert!(!Name::from_str("www.example").unwrap().is_fqdn());
assert!(!Name::from_str("www").unwrap().is_fqdn());
} }
} }

View File

@@ -659,10 +659,11 @@ pub enum RData {
impl RData { impl RData {
/// Parse the RData from a set of Tokens /// Parse the RData from a set of Tokens
pub fn parse(record_type: RecordType, pub fn parse(
tokens: &Vec<Token>, record_type: RecordType,
origin: Option<&Name>) tokens: &Vec<Token>,
-> ParseResult<Self> { origin: Option<&Name>,
) -> ParseResult<Self> {
let rdata = match record_type { let rdata = match record_type {
RecordType::A => RData::A(try!(rdata::a::parse(tokens))), RecordType::A => RData::A(try!(rdata::a::parse(tokens))),
RecordType::AAAA => RData::AAAA(try!(rdata::aaaa::parse(tokens))), RecordType::AAAA => RData::AAAA(try!(rdata::aaaa::parse(tokens))),
@@ -695,20 +696,20 @@ impl RData {
let mut buf: Vec<u8> = Vec::new(); let mut buf: Vec<u8> = Vec::new();
{ {
let mut encoder: BinEncoder = BinEncoder::new(&mut buf); let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
self.emit(&mut encoder) self.emit(&mut encoder).unwrap_or_else(|_| {
.unwrap_or_else(|_| { warn!("could not encode RDATA: {:?}", self);
warn!("could not encode RDATA: {:?}", self); ()
() });
});
} }
buf buf
} }
/// Read the RData from the given Decoder /// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder, pub fn read(
record_type: RecordType, decoder: &mut BinDecoder,
rdata_length: u16) record_type: RecordType,
-> DecodeResult<Self> { rdata_length: u16,
) -> DecodeResult<Self> {
let start_idx = decoder.index(); let start_idx = decoder.index();
let result = match record_type { let result = match record_type {
@@ -802,8 +803,9 @@ impl RData {
// we should have read rdata_length, but we did not // we should have read rdata_length, but we did not
let read = decoder.index() - start_idx; let read = decoder.index() - start_idx;
if read != rdata_length as usize { if read != rdata_length as usize {
return Err(DecodeErrorKind::IncorrectRDataLengthRead(read, rdata_length as usize) return Err(
.into()); DecodeErrorKind::IncorrectRDataLengthRead(read, rdata_length as usize).into(),
);
} }
Ok(result) Ok(result)
} }
@@ -953,125 +955,263 @@ mod tests {
use rr::rdata::{MX, SOA, SRV, TXT}; use rr::rdata::{MX, SOA, SRV, TXT};
fn get_data() -> Vec<(RData, Vec<u8>)> { fn get_data() -> Vec<(RData, Vec<u8>)> {
vec![(RData::CNAME(Name::with_labels(vec!["www".to_string(), vec![
"example".to_string(), (
"com".to_string()])), RData::CNAME(Name::from_labels(vec!["www", "example", "com"])),
vec![3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', vec![
b'o', b'm', 0]), 3,
(RData::MX(MX::new(256, Name::with_labels(vec!["n".to_string()]))), b'w',
vec![1, 0, 1, b'n', 0]), b'w',
(RData::NS(Name::with_labels(vec!["www".to_string(), b'w',
"example".to_string(), 7,
"com".to_string()])), b'e',
vec![3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'x',
b'o', b'm', 0]), b'a',
(RData::PTR(Name::with_labels(vec!["www".to_string(), b'm',
"example".to_string(), b'p',
"com".to_string()])), b'l',
vec![3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'e',
b'o', b'm', 0]), 3,
(RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(), b'c',
"example".to_string(), b'o',
"com".to_string()]), b'm',
Name::with_labels(vec!["xxx".to_string(), 0,
"example".to_string(), ]
"com".to_string()]), ),
u32::max_value(), (
-1 as i32, RData::MX(MX::new(256, Name::from_labels(vec!["n"]))),
-1 as i32, vec![1, 0, 1, b'n', 0]
-1 as i32, ),
u32::max_value())), (
vec![3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', RData::NS(Name::from_labels(vec!["www", "example", "com"])),
b'o', b'm', 0, 3, b'x', b'x', b'x', 0xC0, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, vec![
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 3,
0xFF, 0xFF]), b'w',
(RData::TXT(TXT::new(vec!["abcdef".to_string(), b'w',
"ghi".to_string(), b'w',
"".to_string(), 7,
"j".to_string()])), b'e',
vec![6, b'a', b'b', b'c', b'd', b'e', b'f', 3, b'g', b'h', b'i', 0, 1, b'j']), b'x',
(RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()), vec![0, 0, 0, 0]), b'a',
(RData::AAAA(Ipv6Addr::from_str("::").unwrap()), b'm',
vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), b'p',
(RData::SRV(SRV::new(1, b'l',
2, b'e',
3, 3,
Name::with_labels(vec!["www".to_string(), b'c',
"example".to_string(), b'o',
"com".to_string()]))), b'm',
vec![0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 3, b'w', b'w', b'w', 7, b'e', b'x', b'a', 0,
b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0])] ]
),
(
RData::PTR(Name::from_labels(vec!["www", "example", "com"])),
vec![
3,
b'w',
b'w',
b'w',
7,
b'e',
b'x',
b'a',
b'm',
b'p',
b'l',
b'e',
3,
b'c',
b'o',
b'm',
0,
]
),
(
RData::SOA(SOA::new(
Name::from_labels(vec!["www", "example", "com"]),
Name::from_labels(vec!["xxx", "example", "com"]),
u32::max_value(),
-1 as i32,
-1 as i32,
-1 as i32,
u32::max_value(),
)),
vec![
3,
b'w',
b'w',
b'w',
7,
b'e',
b'x',
b'a',
b'm',
b'p',
b'l',
b'e',
3,
b'c',
b'o',
b'm',
0,
3,
b'x',
b'x',
b'x',
0xC0,
0x04,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
]
),
(
RData::TXT(TXT::new(vec![
"abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"j".to_string(),
])),
vec![
6,
b'a',
b'b',
b'c',
b'd',
b'e',
b'f',
3,
b'g',
b'h',
b'i',
0,
1,
b'j',
]
),
(
RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
vec![0, 0, 0, 0]
),
(
RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
),
(
RData::SRV(SRV::new(
1,
2,
3,
Name::from_labels(vec!["www", "example", "com"]),
)),
vec![
0x00,
0x01,
0x00,
0x02,
0x00,
0x03,
3,
b'w',
b'w',
b'w',
7,
b'e',
b'x',
b'a',
b'm',
b'p',
b'l',
b'e',
3,
b'c',
b'o',
b'm',
0,
]
),
]
} }
// TODO this test kinda sucks, shows the problem with not storing the binary parts // TODO this test kinda sucks, shows the problem with not storing the binary parts
#[test] #[test]
fn test_order() { fn test_order() {
let ordered: Vec<RData> = vec![RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()), let ordered: Vec<RData> =
RData::AAAA(Ipv6Addr::from_str("::").unwrap()), vec![
RData::SRV(SRV::new(1, RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
2, RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
3, RData::SRV(SRV::new(
Name::with_labels(vec!["www".to_string(), 1,
"example".to_string(), 2,
"com".to_string()]))), 3,
RData::MX(MX::new(256, Name::with_labels(vec!["n".to_string()]))), Name::from_labels(vec!["www", "example", "com"]),
RData::CNAME(Name::with_labels(vec!["www".to_string(), )),
"example".to_string(), RData::MX(MX::new(256, Name::from_labels(vec!["n"]))),
"com".to_string()])), RData::CNAME(Name::from_labels(vec!["www", "example", "com"])),
RData::PTR(Name::with_labels(vec!["www".to_string(), RData::PTR(Name::from_labels(vec!["www", "example", "com"])),
"example".to_string(), RData::NS(Name::from_labels(vec!["www", "example", "com"])),
"com".to_string()])), RData::SOA(SOA::new(
RData::NS(Name::with_labels(vec!["www".to_string(), Name::from_labels(vec!["www", "example", "com"]),
"example".to_string(), Name::from_labels(vec!["xxx", "example", "com"]),
"com".to_string()])), u32::max_value(),
RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(), -1 as i32,
"example".to_string(), -1 as i32,
"com".to_string()]), -1 as i32,
Name::with_labels(vec!["xxx".to_string(), u32::max_value(),
"example".to_string(), )),
"com".to_string()]), RData::TXT(TXT::new(vec![
u32::max_value(), "abcdef".to_string(),
-1 as i32, "ghi".to_string(),
-1 as i32, "".to_string(),
-1 as i32, "j".to_string(),
u32::max_value())), ])),
RData::TXT(TXT::new(vec!["abcdef".to_string(), ];
"ghi".to_string(), let mut unordered = vec![
"".to_string(), RData::CNAME(Name::from_labels(vec!["www", "example", "com"])),
"j".to_string()]))]; RData::MX(MX::new(256, Name::from_labels(vec!["n"]))),
let mut unordered = vec![RData::CNAME(Name::with_labels(vec!["www".to_string(), RData::PTR(Name::from_labels(vec!["www", "example", "com"])),
"example".to_string(), RData::NS(Name::from_labels(vec!["www", "example", "com"])),
"com".to_string()])), RData::SOA(SOA::new(
RData::MX(MX::new(256, Name::with_labels(vec!["n".to_string()]))), Name::from_labels(vec!["www", "example", "com"]),
RData::PTR(Name::with_labels(vec!["www".to_string(), Name::from_labels(vec!["xxx", "example", "com"]),
"example".to_string(), u32::max_value(),
"com".to_string()])), -1 as i32,
RData::NS(Name::with_labels(vec!["www".to_string(), -1 as i32,
"example".to_string(), -1 as i32,
"com".to_string()])), u32::max_value(),
RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(), )),
"example".to_string(), RData::TXT(TXT::new(vec![
"com".to_string()]), "abcdef".to_string(),
Name::with_labels(vec!["xxx".to_string(), "ghi".to_string(),
"example".to_string(), "".to_string(),
"com".to_string()]), "j".to_string(),
u32::max_value(), ])),
-1 as i32, RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
-1 as i32, RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
-1 as i32, RData::SRV(SRV::new(
u32::max_value())), 1,
RData::TXT(TXT::new(vec!["abcdef".to_string(), 2,
"ghi".to_string(), 3,
"".to_string(), Name::from_labels(vec!["www", "example", "com"]),
"j".to_string()])), )),
RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()), ];
RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
RData::SRV(SRV::new(1,
2,
3,
Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])))];
unordered.sort(); unordered.sort();
assert_eq!(ordered, unordered); assert_eq!(ordered, unordered);
@@ -1086,11 +1226,14 @@ mod tests {
let length = binary.len() as u16; // pre exclusive borrow let length = binary.len() as u16; // pre exclusive borrow
let mut decoder = BinDecoder::new(&binary); let mut decoder = BinDecoder::new(&binary);
assert_eq!(RData::read(&mut decoder, assert_eq!(
::rr::record_type::RecordType::from(&expect), RData::read(
length) &mut decoder,
.unwrap(), ::rr::record_type::RecordType::from(&expect),
expect); length,
).unwrap(),
expect
);
} }
} }