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.
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
### 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.
#[derive(Debug, Eq, Clone)]
pub struct Name {
is_fqdn: bool,
labels: Rc<Vec<Rc<String>>>,
}
impl Name {
/// Create a new domain::Name, i.e. label
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.
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.
@ -63,6 +69,32 @@ impl Name {
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
pub fn label(mut self, label: &'static str) -> Self {
// TODO get_mut() on Arc was unstable when this was written
@ -86,14 +118,17 @@ impl Name {
/// use trust_dns::rr::domain::Name;
///
/// 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![]);
/// assert!(root.is_root());
/// ```
pub fn from_labels<S: Into<String>>(labels: Vec<S>) -> Self {
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`
@ -112,7 +147,10 @@ impl Name {
}
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
@ -145,13 +183,15 @@ impl Name {
/// 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);
/// ```
pub fn to_lowercase(&self) -> Name {
pub fn to_lowercase(&self) -> Self {
let mut new_labels = Vec::with_capacity(self.labels.len());
for label in self.labels.iter() {
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
@ -190,7 +230,10 @@ impl Name {
pub fn trim_to(&self, num_labels: usize) -> Name {
if 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 {
Self::root()
}
@ -282,6 +325,7 @@ impl Name {
// short cirtuit root parse
if local == "." {
name.set_fqdn(true);
return Ok(name);
}
@ -363,10 +407,14 @@ impl Name {
// }
// }
if !local.ends_with('.') {
name.append(try!(origin.ok_or(ParseError::from(
ParseErrorKind::Message("$ORIGIN was not specified"),
))));
// FIXME: mark as FQDN or not_FQDN
if local.ends_with('.') {
name.set_fqdn(true);
} else {
if let Some(other) = origin {
name.append(other);
name.set_fqdn(true);
}
}
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 {
@ -738,7 +789,7 @@ mod tests {
}
#[test]
fn num_labels() {
fn test_num_labels() {
assert_eq!(Name::new().label("*").num_labels(), 0);
assert_eq!(Name::new().label("a").num_labels(), 1);
assert_eq!(Name::new().label("*").label("b").num_labels(), 1);
@ -748,12 +799,12 @@ mod tests {
}
#[test]
fn parse() {
fn test_read() {
test_read_data_set(get_data(), |ref mut d| Name::read(d));
}
#[test]
fn write_to() {
fn test_write_to() {
test_emit_data_set(get_data(), |e, n| n.emit(e));
}
@ -843,7 +894,7 @@ mod tests {
#[test]
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![
(root.clone().unwrap(), root.clone().unwrap()),
(
@ -860,7 +911,7 @@ mod tests {
#[test]
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)> =
vec![
(
@ -905,7 +956,7 @@ mod tests {
#[test]
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![
(
Name::parse("ExAmPle", root.as_ref()).unwrap(),
@ -985,6 +1036,19 @@ mod tests {
Name::from_str("www.example.com.").unwrap(),
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 {
/// Parse the RData from a set of Tokens
pub fn parse(record_type: RecordType,
tokens: &Vec<Token>,
origin: Option<&Name>)
-> ParseResult<Self> {
pub fn parse(
record_type: RecordType,
tokens: &Vec<Token>,
origin: Option<&Name>,
) -> ParseResult<Self> {
let rdata = match record_type {
RecordType::A => RData::A(try!(rdata::a::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 encoder: BinEncoder = BinEncoder::new(&mut buf);
self.emit(&mut encoder)
.unwrap_or_else(|_| {
warn!("could not encode RDATA: {:?}", self);
()
});
self.emit(&mut encoder).unwrap_or_else(|_| {
warn!("could not encode RDATA: {:?}", self);
()
});
}
buf
}
/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder,
record_type: RecordType,
rdata_length: u16)
-> DecodeResult<Self> {
pub fn read(
decoder: &mut BinDecoder,
record_type: RecordType,
rdata_length: u16,
) -> DecodeResult<Self> {
let start_idx = decoder.index();
let result = match record_type {
@ -802,8 +803,9 @@ impl RData {
// we should have read rdata_length, but we did not
let read = decoder.index() - start_idx;
if read != rdata_length as usize {
return Err(DecodeErrorKind::IncorrectRDataLengthRead(read, rdata_length as usize)
.into());
return Err(
DecodeErrorKind::IncorrectRDataLengthRead(read, rdata_length as usize).into(),
);
}
Ok(result)
}
@ -953,125 +955,263 @@ mod tests {
use rr::rdata::{MX, SOA, SRV, TXT};
fn get_data() -> Vec<(RData, Vec<u8>)> {
vec![(RData::CNAME(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
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::MX(MX::new(256, Name::with_labels(vec!["n".to_string()]))),
vec![1, 0, 1, b'n', 0]),
(RData::NS(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
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::PTR(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
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::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()]),
Name::with_labels(vec!["xxx".to_string(),
"example".to_string(),
"com".to_string()]),
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::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()]))),
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])]
vec![
(
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',
b'o',
b'm',
0,
]
),
(
RData::MX(MX::new(256, Name::from_labels(vec!["n"]))),
vec![1, 0, 1, b'n', 0]
),
(
RData::NS(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::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
#[test]
fn test_order() {
let ordered: Vec<RData> = vec![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()]))),
RData::MX(MX::new(256, Name::with_labels(vec!["n".to_string()]))),
RData::CNAME(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
RData::PTR(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
RData::NS(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()]),
Name::with_labels(vec!["xxx".to_string(),
"example".to_string(),
"com".to_string()]),
u32::max_value(),
-1 as i32,
-1 as i32,
-1 as i32,
u32::max_value())),
RData::TXT(TXT::new(vec!["abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"j".to_string()]))];
let mut unordered = vec![RData::CNAME(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
RData::MX(MX::new(256, Name::with_labels(vec!["n".to_string()]))),
RData::PTR(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
RData::NS(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()])),
RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(),
"example".to_string(),
"com".to_string()]),
Name::with_labels(vec!["xxx".to_string(),
"example".to_string(),
"com".to_string()]),
u32::max_value(),
-1 as i32,
-1 as i32,
-1 as i32,
u32::max_value())),
RData::TXT(TXT::new(vec!["abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"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()])))];
let ordered: Vec<RData> =
vec![
RData::A(Ipv4Addr::from_str("0.0.0.0").unwrap()),
RData::AAAA(Ipv6Addr::from_str("::").unwrap()),
RData::SRV(SRV::new(
1,
2,
3,
Name::from_labels(vec!["www", "example", "com"]),
)),
RData::MX(MX::new(256, Name::from_labels(vec!["n"]))),
RData::CNAME(Name::from_labels(vec!["www", "example", "com"])),
RData::PTR(Name::from_labels(vec!["www", "example", "com"])),
RData::NS(Name::from_labels(vec!["www", "example", "com"])),
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(),
)),
RData::TXT(TXT::new(vec![
"abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"j".to_string(),
])),
];
let mut unordered = vec![
RData::CNAME(Name::from_labels(vec!["www", "example", "com"])),
RData::MX(MX::new(256, Name::from_labels(vec!["n"]))),
RData::PTR(Name::from_labels(vec!["www", "example", "com"])),
RData::NS(Name::from_labels(vec!["www", "example", "com"])),
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(),
)),
RData::TXT(TXT::new(vec![
"abcdef".to_string(),
"ghi".to_string(),
"".to_string(),
"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::from_labels(vec!["www", "example", "com"]),
)),
];
unordered.sort();
assert_eq!(ordered, unordered);
@ -1086,11 +1226,14 @@ mod tests {
let length = binary.len() as u16; // pre exclusive borrow
let mut decoder = BinDecoder::new(&binary);
assert_eq!(RData::read(&mut decoder,
::rr::record_type::RecordType::from(&expect),
length)
.unwrap(),
expect);
assert_eq!(
RData::read(
&mut decoder,
::rr::record_type::RecordType::from(&expect),
length,
).unwrap(),
expect
);
}
}