better tests, and new writing routines

This commit is contained in:
Benjamin Fry 2015-08-16 16:51:52 -07:00
parent 102bed0d1c
commit 7adbc23d64
12 changed files with 219 additions and 78 deletions

View File

@ -46,7 +46,7 @@ pub struct Query {
impl Query {
pub fn parse(data: &mut Vec<u8>) -> Self {
let name = Name::parse(data).unwrap();
let name = Name::parse(data);
let query_type = RecordType::parse(data);
let query_class = DNSClass::parse(data);

View File

@ -3,6 +3,7 @@ use std::ops::Index;
use super::util;
#[derive(Debug, PartialEq)]
pub struct Name {
labels: Vec<String>
}
@ -12,7 +13,7 @@ impl Name {
/// this has a max of 255 octets, with each label being less than 63.
/// all names will be stored lowercase internally.
/// This will consume the portions of the Vec which it is reading...
pub fn parse(slice: &mut Vec<u8>) -> Result<Name, FromUtf8Error> {
pub fn parse(slice: &mut Vec<u8>) -> Name {
let mut state: LabelParseState = LabelParseState::LabelLengthOrPointer;
let mut labels: Vec<String> = Vec::with_capacity(3); // most labels will be around three, e.g. www.example.com
@ -25,35 +26,47 @@ impl Name {
state = match state {
LabelParseState::LabelLengthOrPointer => {
// determine what the next label is
match slice.pop() {
Some(0) | None => LabelParseState::Root,
Some(byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer(byte & 0x3F),
Some(byte) if byte <= 0x3F => LabelParseState::Label(byte),
match slice.last() {
Some(&0) | None => LabelParseState::Root,
Some(&byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer,
Some(&byte) if byte <= 0x3F => LabelParseState::Label,
_ => unimplemented!(),
}
},
LabelParseState::Label(count) => {
labels.push(try!(util::parse_label(slice, count)));
LabelParseState::Label => {
labels.push(util::parse_character_data(slice));
// reset to collect more data
LabelParseState::LabelLengthOrPointer
},
LabelParseState::Pointer(offset) => {
LabelParseState::Pointer => {
// lookup in the hashmap the label to use
unimplemented!()
},
LabelParseState::Root => {
// technically could return here...
// need to pop() the 0 off the stack...
slice.pop();
break;
}
}
}
Ok(Name { labels: labels })
Name { labels: labels }
}
pub fn write_to(&self, buf: &mut Vec<u8>) {
let buf_len = buf.len(); // lazily assert the size is less than 255...
for label in &self.labels {
util::write_character_data_to(buf, label);
assert!((buf.len() - buf_len) <= 63); // individual labels must be shorter than 63.
}
// the end of the list of names
buf.push(0);
assert!((buf.len() - buf_len) <= 255); // the entire name needs to be less than 256.
}
}
impl Index<usize> for Name {
type Output = String;
@ -65,35 +78,38 @@ impl Index<usize> for Name {
/// This is the list of states for the label parsing state machine
enum LabelParseState {
LabelLengthOrPointer, // basically the start of the FSM
Label(u8), // storing length of the label, must be < 63
Pointer(u8), // location of pointer in slice,
Root, // root is the end of the labels list, aka null
Label, // storing length of the label, must be < 63
Pointer, // location of pointer in slice,
Root, // root is the end of the labels list, aka null
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::util::tests::{test_parse_data_set, test_write_data_set_to};
#[test]
fn parse() {
let data: Vec<(Vec<u8>, Vec<String>)> = vec![
(vec![0], vec![]), // base case, only the root
(vec![1,b'a',0], vec!["a".to_string()]), // a single 'a' label
(vec![1,b'a',2,b'b',b'c',0], vec!["a".to_string(), "bc".to_string()]), // two labels, 'a.bc'
(vec![1,b'a',3,0xE2,0x99,0xA5,0], vec!["a".to_string(), "".to_string()]), // two labels utf8, 'a.♥'
(vec![1,b'A',0], vec!["a".to_string()]), // a single 'a' label, lowercased
let data: Vec<(Vec<u8>, Name)> = vec![
(vec![0], Name{ labels: vec![] }), // base case, only the root
(vec![1,b'a',0], Name{ labels: vec!["a".to_string()] }), // a single 'a' label
(vec![1,b'a',2,b'b',b'c',0], Name{ labels: vec!["a".to_string(), "bc".to_string()] }), // two labels, 'a.bc'
(vec![1,b'a',3,0xE2,0x99,0xA5,0], Name{ labels: vec!["a".to_string(), "".to_string()] }), // two labels utf8, 'a.♥'
(vec![1,b'A',0], Name { labels: vec!["a".to_string()] }), // a single 'a' label, lowercased
];
let mut test_num = 0;
for (mut binary, expect) in data {
test_num += 1;
println!("test: {}", test_num);
binary.reverse();
let name = Name::parse(&mut binary).ok().unwrap();
assert_eq!(name.labels, expect);
if expect.len() > 0 {
assert_eq!(name[0], expect[0]);
}
}
test_parse_data_set(data, |b| Name::parse(b));
}
#[test]
fn write_to() {
let data: Vec<(Name, Vec<u8>)> = vec![
(Name { labels: vec![] }, vec![0]), // base case, only the root
(Name { labels: vec!["a".to_string()] }, vec![1,b'a',0]), // a single 'a' label
(Name { labels: vec!["a".to_string(), "bc".to_string()] }, vec![1,b'a',2,b'b',b'c',0]), // two labels, 'a.bc'
(Name { labels: vec!["a".to_string(), "".to_string()] }, vec![1,b'a',3,0xE2,0x99,0xA5,0]), // two labels utf8, 'a.♥'
];
test_write_data_set_to(data, |b, n| n.write_to(b));
}
}

View File

@ -19,7 +19,7 @@ use super::super::domain::Name;
//
// CNAME { cname: Name },
pub fn parse(data: &mut Vec<u8>) -> RData {
RData::CNAME{ cname: Name::parse(data).unwrap() }
RData::CNAME{ cname: Name::parse(data) }
}
#[test]

View File

@ -24,7 +24,7 @@ use super::super::util;
//
// HINFO { cpu: String, os: String},
pub fn parse(data: &mut Vec<u8>) -> RData {
RData::HINFO { cpu: util::parse_character_data(data).unwrap(), os: util::parse_character_data(data).unwrap() }
RData::HINFO { cpu: util::parse_character_data(data), os: util::parse_character_data(data)}
}
#[test]

View File

@ -26,7 +26,7 @@ use super::super::domain::Name;
//
// MX { preference: u16, exchange: Name },
pub fn parse(data: &mut Vec<u8>) -> RData {
RData::MX { preference: util::parse_u16(data), exchange: Name::parse(data).unwrap() }
RData::MX { preference: util::parse_u16(data), exchange: Name::parse(data) }
}
#[test]

View File

@ -26,7 +26,7 @@ use super::super::domain::Name;
//
// NS { nsdname: Name },
pub fn parse(data: &mut Vec<u8>) -> RData {
RData::NS{ nsdname: Name::parse(data).unwrap() }
RData::NS{ nsdname: Name::parse(data) }
}
#[test]

View File

@ -20,7 +20,7 @@ use super::super::domain::Name;
//
// PTR { ptrdname: Name },
pub fn parse(data: &mut Vec<u8>) -> RData {
RData::PTR{ ptrdname: Name::parse(data).unwrap() }
RData::PTR{ ptrdname: Name::parse(data) }
}
#[test]

View File

@ -70,8 +70,8 @@ use super::super::domain::Name;
// SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
pub fn parse(data: &mut Vec<u8>) -> RData {
RData::SOA{
mname: Name::parse(data).unwrap(),
rname: Name::parse(data).unwrap(),
mname: Name::parse(data),
rname: Name::parse(data),
serial: util::parse_u32(data),
refresh: util::parse_i32(data),
retry: util::parse_i32(data),

View File

@ -22,7 +22,7 @@ pub fn parse(data: &mut Vec<u8>, count: u16) -> RData {
let mut strings = Vec::with_capacity(1);
while data_len - data.len() < count as usize {
util::parse_character_data(data).and_then(|s: String| -> Result<(), FromUtf8Error> { strings.push(s); Ok(()) });
strings.push(util::parse_character_data(data));
}
RData::TXT{ txt_data: strings }
}

View File

@ -51,6 +51,10 @@ impl RecordType {
pub fn parse(data: &mut Vec<u8>) -> Self {
util::parse_u16(data).into()
}
pub fn write_to(buf: &mut Vec<u8>) {
}
}

View File

@ -30,7 +30,7 @@ impl Record {
// NAME an owner name, i.e., the name of the node to which this
// resource record pertains.
let name_labels: domain::Name = try!(domain::Name::parse(&mut data));
let name_labels: domain::Name = domain::Name::parse(&mut data);
// TYPE two octets containing one of the RR TYPE codes.
let record_type: RecordType = RecordType::parse(&mut data);

View File

@ -1,22 +1,17 @@
use std::string::FromUtf8Error;
// TODO: !!! Need to convert to an internally stored [] and not destroy on read,
// so that "pointer" types can be handled
///<character-string> is a single
/// length octet followed by that number of characters. <character-string>
/// is treated as binary information, and can be up to 256 characters in
/// length (including the length octet).
///
/// the vector should be reversed before calling.
pub fn parse_character_data(data: &mut Vec<u8>) -> Result<String, FromUtf8Error> {
let length: u8 = data.pop().unwrap_or(0);
parse_label(data, length)
}
/// parse a label of a particular length (it's a portion of the vector)
/// the vector should be reversed before calling.
///```
/// assert_eq(parse_lable(b"bbbaaa", 6).ok().unwrap(), "aaabbb".to_string());
///```
pub fn parse_label(data: &mut Vec<u8>, length: u8) -> Result<String, FromUtf8Error> {
pub fn parse_character_data(data: &mut Vec<u8>) -> String {
let length: u8 = data.pop().unwrap();
assert!(length <= 255u8);
// TODO once Drain stabalizes on Vec, this should be replaced...
let mut label_vec: Vec<u8> = Vec::with_capacity(length as usize);
for _ in 0..length as usize {
@ -27,16 +22,38 @@ pub fn parse_label(data: &mut Vec<u8>, length: u8) -> Result<String, FromUtf8Err
}
// translate bytes to string, then lowercase...
Ok(try!(String::from_utf8(label_vec)).to_lowercase())
String::from_utf8(label_vec).unwrap().to_lowercase()
}
/// matches description from above.
///
/// ```
/// use trust_dns::rr::util;
///
/// let mut buf: Vec<u8> = Vec::new();
/// util::write_character_data_to(&mut buf, "abc");
/// assert_eq!(buf, vec![3,b'a',b'b',b'c']);
/// ```
pub fn write_character_data_to(buf: &mut Vec<u8>, char_data: &str) {
let char_bytes = char_data.as_bytes();
assert!(char_bytes.len() < 256);
buf.reserve(char_bytes.len()+1); // reserve the full space for the string
buf.push(char_bytes.len() as u8);
// a separate writer isn't necessary for label since it's the same first byte that's being written
// TODO use append() once it stabalizes
for b in char_bytes {
buf.push(*b);
}
}
/// parses the next 2 bytes into u16. This performs a byte-by-byte manipulation, there
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
///
/// the vector should be reversed before calling.
pub fn parse_u16(data: &mut Vec<u8>) -> u16 {
// TODO should this use a default rather than the panic! that will happen in the None case?
let b1: u8 = data.pop().unwrap();
let b2: u8 = data.pop().unwrap();
@ -44,6 +61,16 @@ pub fn parse_u16(data: &mut Vec<u8>) -> u16 {
((b1 as u16) << 8) + (b2 as u16)
}
pub fn write_u16_to(buf: &mut Vec<u8>, data: u16) {
buf.reserve(2); // two bytes coming
let b1: u8 = (data >> 8 & 0xFF) as u8;
let b2: u8 = (data & 0xFF) as u8;
buf.push(b1);
buf.push(b2);
}
/// parses the next four bytes into i32. This performs a byte-by-byte manipulation, there
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
///
@ -59,6 +86,20 @@ pub fn parse_i32(data: &mut Vec<u8>) -> i32 {
((b1 as i32) << 24) + ((b2 as i32) << 16) + ((b3 as i32) << 8) + (b4 as i32)
}
pub fn write_i32_to(buf: &mut Vec<u8>, data: i32) {
buf.reserve(4); // four bytes coming...
let b1: u8 = (data >> 24 & 0xFF) as u8;
let b2: u8 = (data >> 16 & 0xFF) as u8;
let b3: u8 = (data >> 8 & 0xFF) as u8;
let b4: u8 = (data & 0xFF) as u8;
buf.push(b1);
buf.push(b2);
buf.push(b3);
buf.push(b4);
}
/// parses the next four bytes into u32. This performs a byte-by-byte manipulation, there
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
///
@ -74,8 +115,24 @@ pub fn parse_u32(data: &mut Vec<u8>) -> u32 {
((b1 as u32) << 24) + ((b2 as u32) << 16) + ((b3 as u32) << 8) + (b4 as u32)
}
pub fn write_u32_to(buf: &mut Vec<u8>, data: u32) {
buf.reserve(4); // four bytes coming...
let b1: u8 = (data >> 24 & 0xFF) as u8;
let b2: u8 = (data >> 16 & 0xFF) as u8;
let b3: u8 = (data >> 8 & 0xFF) as u8;
let b4: u8 = (data & 0xFF) as u8;
buf.push(b1);
buf.push(b2);
buf.push(b3);
buf.push(b4);
}
#[cfg(test)]
mod tests {
pub mod tests {
use std::fmt::Debug;
#[test]
fn parse_character_data() {
let data: Vec<(Vec<u8>, String)> = vec![
@ -86,12 +143,25 @@ mod tests {
(vec![1,b'A'], "a".to_string()), // a single 'a' label, lowercased
];
test_parse_data_set(data, |b| super::parse_character_data(b));
}
#[test]
fn write_character_data() {
let data: Vec<(&'static str, Vec<u8>)> = vec![
("", vec![0]), // base case, only the root
("a", vec![1,b'a']), // a single 'a' label
("bc", vec![2,b'b',b'c']), // two labels, 'a.bc'
("", vec![3,0xE2,0x99,0xA5]), // two labels utf8, 'a.♥'
];
let mut test_num = 0;
for (mut binary, expect) in data {
for (data, expect) in data {
test_num += 1;
println!("test: {}", test_num);
binary.reverse();
assert_eq!(super::parse_character_data(&mut binary).ok().unwrap(), expect);
println!("test {}: {:?}", test_num, data);
let mut buf: Vec<u8> = Vec::with_capacity(expect.len());
super::write_character_data_to(&mut buf, data);
assert_eq!(buf, expect);
}
}
@ -104,13 +174,19 @@ mod tests {
(vec![0xFF,0xFF], u16::max_value()),
];
let mut test_num = 0;
for (mut binary, expect) in data {
test_num += 1;
println!("test: {}", test_num);
binary.reverse();
assert_eq!(super::parse_u16(&mut binary), expect);
}
test_parse_data_set(data, |b| super::parse_u16(b));
}
#[test]
fn write_u16() {
let data: Vec<(u16, Vec<u8>)> = vec![
(0, vec![0x00,0x00]),
(1, vec![0x00,0x01]),
(256, vec![0x01,0x00]),
(u16::max_value(), vec![0xFF,0xFF]),
];
test_write_data_set_to(data, |b, d| super::write_u16_to(b,d));
}
#[test]
@ -126,13 +202,23 @@ mod tests {
(vec![0x7F,0xFF,0xFF,0xFF], i32::max_value()),
];
let mut test_num = 0;
for (mut binary, expect) in data {
test_num += 1;
println!("test: {}", test_num);
binary.reverse();
assert_eq!(super::parse_i32(&mut binary), expect);
}
test_parse_data_set(data, |b| super::parse_i32(b));
}
#[test]
fn write_i32() {
let data: Vec<(i32, Vec<u8>)> = vec![
(0, vec![0x00,0x00,0x00,0x00]),
(1, vec![0x00,0x00,0x00,0x01]),
(256, vec![0x00,0x00,0x01,0x00]),
(256*256, vec![0x00,0x01,0x00,0x00]),
(256*256*256, vec![0x01,0x00,0x00,0x00]),
(-1, vec![0xFF,0xFF,0xFF,0xFF]),
(i32::min_value(), vec![0x80,0x00,0x00,0x00]),
(i32::max_value(), vec![0x7F,0xFF,0xFF,0xFF]),
];
test_write_data_set_to(data, |b, d| super::write_i32_to(b,d));
}
#[test]
@ -148,12 +234,47 @@ mod tests {
(vec![0x7F,0xFF,0xFF,0xFF], i32::max_value() as u32),
];
let mut test_num = 0;
for (mut binary, expect) in data {
test_num += 1;
println!("test: {} binary: {:?} expect: {:?}", test_num, binary, expect);
test_parse_data_set(data, |b| super::parse_u32(b));
}
#[test]
fn write_u32() {
let data: Vec<(u32, Vec<u8>)> = vec![
(0, vec![0x00,0x00,0x00,0x00]),
(1, vec![0x00,0x00,0x00,0x01]),
(256, vec![0x00,0x00,0x01,0x00]),
(256*256, vec![0x00,0x01,0x00,0x00]),
(256*256*256, vec![0x01,0x00,0x00,0x00]),
(u32::max_value(), vec![0xFF,0xFF,0xFF,0xFF]),
(2147483648, vec![0x80,0x00,0x00,0x00]),
(i32::max_value() as u32, vec![0x7F,0xFF,0xFF,0xFF]),
];
test_write_data_set_to(data, |b, d| super::write_u32_to(b,d));
}
pub fn test_parse_data_set<E, F>(data_set: Vec<(Vec<u8>, E)>, parse_func: F)
where E: PartialEq<E> + Debug, F: Fn(&mut Vec<u8>) -> E {
let mut test_pass = 0;
for (mut binary, expect) in data_set {
test_pass += 1;
println!("test {}: {:?}", test_pass, binary);
binary.reverse();
assert_eq!(super::parse_u32(&mut binary), expect);
assert_eq!(parse_func(&mut binary), expect);
}
}
pub fn test_write_data_set_to<S, F>(data_set: Vec<(S, Vec<u8>)>, write_func: F)
where F: Fn(&mut Vec<u8>, S), S: Debug {
let mut test_pass = 0;
for (data, expect) in data_set {
test_pass += 1;
println!("test {}: {:?}", test_pass, data);
let mut buf: Vec<u8> = Vec::with_capacity(expect.len());
write_func(&mut buf, data);
assert_eq!(buf, expect);
}
}
}